├── .gitignore
├── LICENSE
├── README.md
├── algorithms.html
├── ambilight.html
├── background.html
├── background_async.html
├── background_async_dominant.html
├── border.html
├── box-shadow-4-sides.html
├── box-shadow.html
├── canvas.html
├── define.html
├── dist
├── algorithms.css
├── algorithms.js
├── ambilight.css
├── ambilight.js
├── background.css
├── background.js
├── background_async.css
├── background_async.js
├── background_async_dominant.css
├── background_async_dominant.js
├── border.css
├── border.js
├── box-shadow-4-sides.css
├── box-shadow-4-sides.js
├── box-shadow.css
├── box-shadow.js
├── canvas.css
├── canvas.js
├── define.css
├── define.js
├── gallery.css
├── gallery.js
├── gallery_vertical.css
├── gallery_vertical.js
├── gradient.css
├── gradient.js
├── gradient_stripes.css
├── gradient_stripes.js
├── text-photo.css
├── text-photo.js
├── timeline.css
└── timeline.js
├── gallery.html
├── gallery_vertical.html
├── gradient.html
├── gradient_stripes.html
├── images
├── 1.jpg
├── 10.jpg
├── 11.jpg
├── 12.jpg
├── 13.jpg
├── 14.jpg
├── 15.jpg
├── 16.jpg
├── 17.jpg
├── 18.jpg
├── 19.jpg
├── 2.jpg
├── 20.jpg
├── 21.jpg
├── 22.jpg
├── 23.jpg
├── 24.jpg
├── 25.jpg
├── 26.jpg
├── 27.jpg
├── 3.jpg
├── 4.jpg
├── 5.jpg
├── 6.jpg
├── 7.jpg
├── 8.jpg
├── 9.jpg
├── chrome.jpg
├── firefox.jpg
├── ie.jpg
├── opera.jpg
└── transparent.png
├── package-lock.json
├── package.json
├── rollup.config.mjs
├── src
├── algorithms
│ ├── build.js
│ ├── index.css
│ ├── index.ts
│ └── template.html
├── ambilight
│ ├── ambilight.ts
│ ├── index.css
│ ├── index.ts
│ └── types
│ │ ├── 4sides.ts
│ │ ├── base.ts
│ │ ├── helpers
│ │ └── ua.ts
│ │ └── many-points.ts
├── background
│ ├── index.css
│ └── index.ts
├── background_async
│ ├── index.css
│ └── index.ts
├── background_async_dominant
│ ├── index.css
│ └── index.ts
├── border
│ ├── index.css
│ └── index.ts
├── box-shadow-4-sides
│ ├── index.css
│ └── index.ts
├── box-shadow
│ ├── index.css
│ └── index.ts
├── canvas
│ ├── helpers.ts
│ ├── index.css
│ └── index.ts
├── common.css
├── common.ts
├── define
│ ├── index.css
│ └── index.ts
├── gallery
│ ├── index.css
│ └── index.ts
├── gallery_vertical
│ ├── getColors.ts
│ ├── index.css
│ └── index.ts
├── gradient
│ ├── index.css
│ └── index.ts
├── gradient_stripes
│ ├── index.css
│ └── index.ts
├── text-photo
│ ├── index.css
│ └── index.ts
└── timeline
│ ├── index.css
│ ├── index.ts
│ └── utils.ts
├── text-photo.html
├── timeline.html
├── tsconfig.json
├── videos
└── 1.mp4
└── webgl.html
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Denis Seleznev
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # [Examples](https://fast-average-color.github.io/examples/background.html) for [Fast Average Color](https://github.com/fast-average-color/fast-average-color)
2 |
3 | - [Background](https://fast-average-color.github.io/examples/background.html)
4 | - [Background async](https://fast-average-color.github.io/examples/background_async.html)
5 | - [Box shadow](https://fast-average-color.github.io/examples/box-shadow.html)
6 | - [Box shadow, 4 sides](https://fast-average-color.github.io/examples/box-shadow-4-sides.html)
7 | - [Border](https://fast-average-color.github.io/examples/border.html)
8 | - [Gallery](https://fast-average-color.github.io/examples/gallery.html)
9 | - [Vertical and horizontal](https://fast-average-color.github.io/examples/gallery_vertical.html)
10 | - [Gradient](https://fast-average-color.github.io/examples/gradient.html)
11 | - [Gradient as stripes](https://fast-average-color.github.io/examples/gradient_stripes.html)
12 | - [Canvas](https://fast-average-color.github.io/examples/canvas.html)
13 | - [Text photo](https://fast-average-color.github.io/examples/text-photo.html)
14 | - [Ambilight](https://fast-average-color.github.io/examples/ambilight.html)
15 | - [Algorithms](https://fast-average-color.github.io/examples/algorithms.html)
16 | - [Define the average color for your images](https://fast-average-color.github.io/examples/define.html)
17 | - [Timeline](https://fast-average-color.github.io/examples/timeline.html)
18 | - [WebGL](https://fast-average-color.github.io/examples/webgl.html)
19 |
--------------------------------------------------------------------------------
/algorithms.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Average color with algorithms
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
Photo
15 |
simple algorithm
16 |
sqrt algorithm
17 |
dominant algorithm
18 |
19 |
20 |

21 |
22 |
23 |
24 |
25 |

26 |
27 |
28 |
29 |
30 |

31 |
32 |
33 |
34 |
35 |

36 |
37 |
38 |
39 |
40 |

41 |
42 |
43 |
44 |
45 |

46 |
47 |
48 |
49 |
50 |

51 |
52 |
53 |
54 |
55 |

56 |
57 |
58 |
59 |
60 |

61 |
62 |
63 |
64 |
65 |

66 |
67 |
68 |
69 |
70 |

71 |
72 |
73 |
74 |
75 |

76 |
77 |
78 |
79 |
80 |

81 |
82 |
83 |
84 |
85 |

86 |
87 |
88 |
89 |
90 |

91 |
92 |
93 |
94 |
95 |

96 |
97 |
98 |
99 |
100 |

101 |
102 |
103 |
104 |
105 |

106 |
107 |
108 |
109 |
110 |

111 |
112 |
113 |
114 |
115 |

116 |
117 |
118 |
119 |
120 |

121 |
122 |
123 |
124 |
125 |

126 |
127 |
128 |
129 |
130 |

131 |
132 |
133 |
134 |
135 |

136 |
137 |
138 |
139 |
140 |

141 |
142 |
143 |
144 |
145 |

146 |
147 |
148 |
149 |
150 |

151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
--------------------------------------------------------------------------------
/ambilight.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Get average color for ambilight
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
Ambilight
13 |
14 |
15 |
16 |
17 |
18 |
19 | Press “Play”
20 | Altair - Fluke | Demoscene
21 |
22 |
23 |
--------------------------------------------------------------------------------
/background.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | get average color for background
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |

13 |
14 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
15 |
16 |
17 |
18 |
19 |

20 |
21 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
22 |
23 |
24 |
25 |
26 |

27 |
28 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
29 |
30 |
31 |
32 |
33 |

34 |
35 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
36 |
37 |
38 |
39 |
40 |

41 |
42 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
43 |
44 |
45 |
46 |
47 |

48 |
49 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/background_async.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Get average color for background, async
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |

13 |
14 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
15 |
16 |
17 |
18 |
19 |

20 |
21 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
22 |
23 |
24 |
25 |
26 |

27 |
28 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
29 |
30 |
31 |
32 |
33 |

34 |
35 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
36 |
37 |
38 |
39 |
40 |

41 |
42 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
43 |
44 |
45 |
46 |
47 |

48 |
49 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/background_async_dominant.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Get average color for background, async
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |

13 |
14 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
15 |
16 |
17 |
18 |
19 |

20 |
21 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
22 |
23 |
24 |
25 |
26 |

27 |
28 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
29 |
30 |
31 |
32 |
33 |

34 |
35 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
36 |
37 |
38 |
39 |
40 |

41 |
42 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
43 |
44 |
45 |
46 |
47 |

48 |
49 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/border.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Get average color for border
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
15 |
16 |
17 |
18 |
19 |
20 |
21 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
22 |
23 |
24 |
25 |
26 |
27 |
28 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
29 |
30 |
31 |
32 |
33 |
34 |
35 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
36 |
37 |
38 |
39 |
40 |
41 |
42 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
43 |
44 |
45 |
46 |
47 |
48 |
49 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/box-shadow-4-sides.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Get average color for box shadow, 4 sides
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |

13 |
14 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
15 |
16 |
17 |
18 |
19 |

20 |
21 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
22 |
23 |
24 |
25 |
26 |

27 |
28 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
29 |
30 |
31 |
32 |
33 |

34 |
35 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
36 |
37 |
38 |
39 |
40 |

41 |
42 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
43 |
44 |
45 |
46 |
47 |

48 |
49 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/box-shadow.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Get average color for box shadow
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |

13 |
14 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
15 |
16 |
17 |
18 |
19 |

20 |
21 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
22 |
23 |
24 |
25 |
26 |

27 |
28 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
29 |
30 |
31 |
32 |
33 |

34 |
35 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
36 |
37 |
38 |
39 |
40 |

41 |
42 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
43 |
44 |
45 |
46 |
47 |

48 |
49 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/canvas.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Get Average Color for canvas
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/define.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Get average color of image
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Select a image to define the average color:
13 | Or a photo from the camera.
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/dist/algorithms.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
58 | .rows {
59 | margin: 50px 0 0 20px;
60 | color: #fff;
61 | }
62 |
63 | .row
64 | {
65 | display: flex;
66 | }
67 |
68 | .item
69 | {
70 | transition: all .3s ease;
71 | flex-basis: 300px;
72 | margin: 20px 20px 0 0;
73 | text-align: center;
74 | }
75 |
76 | .item_image {
77 | max-width: 300px;
78 | width: 25%;
79 | }
80 |
81 |
--------------------------------------------------------------------------------
/dist/ambilight.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
58 | .ambilight-control
59 | {
60 | font-size: 12px;
61 | line-height: 12px;
62 |
63 | position: fixed;
64 | z-index: 100;
65 | top: 21px;
66 | left: 50%;
67 |
68 | transform: translateX(-50%);
69 | text-align: center;
70 | white-space: nowrap;
71 |
72 | color: #999;
73 | }
74 |
75 | .ambilight-control h1
76 | {
77 | font-weight: normal;
78 |
79 | margin: 0 0 15px 0;
80 | padding: 0;
81 |
82 | color: #aaa;
83 | }
84 |
85 | .ambilight-control label
86 | {
87 | margin: 0;
88 | margin-right: 8px;
89 | padding: 0;
90 | }
91 |
92 | .ambilight-control input
93 | {
94 | font-size: 12px;
95 | line-height: 12px;
96 |
97 | margin: 0 2px 0 0;
98 |
99 | vertical-align: top;
100 | }
101 |
102 | .video-container
103 | {
104 | position: absolute;
105 | top: 50%;
106 | left: 50%;
107 |
108 | transform: translate(-50%, -50%);
109 | }
110 |
111 | video
112 | {
113 | position: relative;
114 | z-index: 100;
115 |
116 | display: block;
117 |
118 | margin: 0;
119 | padding: 0;
120 |
121 | border: 10px solid black;
122 | outline: 1px solid rgba(255, 255, 255, .1);
123 | background: black;
124 | }
125 |
126 | .video-shadow
127 | {
128 | position: absolute;
129 | }
130 |
131 | .video-shadow_left
132 | {
133 | left: 0;
134 |
135 | border-radius: 100% 0 0 100%;
136 | }
137 |
138 | .video-shadow_right
139 | {
140 | right: 0;
141 |
142 | border-radius: 0 100% 100% 0;
143 | }
144 |
145 | .video-shadow_top
146 | {
147 | top: 0;
148 |
149 | border-radius: 100% 100% 0 0;
150 | }
151 |
152 | .video-shadow_bottom
153 | {
154 | bottom: 0;
155 |
156 | border-radius: 0 0 100% 100%;
157 | }
158 |
159 | .copyright
160 | {
161 | font-family: Arial;
162 | font-size: 12px;
163 |
164 | position: fixed;
165 | bottom: 5px;
166 | left: 50%;
167 |
168 | margin-left: -5em;
169 | }
170 |
171 | .hint
172 | {
173 | font-size: 16px;
174 |
175 | position: absolute;
176 | top: 50%;
177 | left: 50%;
178 |
179 | margin-top: 200px;
180 | margin-left: -320px;
181 |
182 | color: #aaa;
183 | }
184 |
185 | .copyright:link,
186 | .copyright:visited,
187 | .copyright:active,
188 | .copyright:hover
189 | {
190 | color: #555;
191 | }
192 |
193 |
--------------------------------------------------------------------------------
/dist/background.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
58 | img
59 | {
60 | width: 100%;
61 | }
62 |
63 | .item
64 | {
65 | float: left;
66 |
67 | box-sizing: border-box;
68 | width: 500px;
69 | max-width: 100%;
70 | padding: 50px 50px 45px 50px;
71 |
72 | transition: background-color .5s ease;
73 | }
74 |
75 | .item__text
76 | {
77 | padding-top: 10px;
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/dist/background.js:
--------------------------------------------------------------------------------
1 | (function (factory) {
2 | typeof define === 'function' && define.amd ? define(factory) :
3 | factory();
4 | })((function () { 'use strict';
5 |
6 | /*! Fast Average Color | © 2025 Denis Seleznev | MIT License | https://github.com/fast-average-color/fast-average-color */
7 | function toHex(num) {
8 | var str = num.toString(16);
9 | return str.length === 1 ? '0' + str : str;
10 | }
11 | function arrayToHex(arr) {
12 | return '#' + arr.map(toHex).join('');
13 | }
14 | function isDark(color) {
15 | // http://www.w3.org/TR/AERT#color-contrast
16 | var result = (color[0] * 299 + color[1] * 587 + color[2] * 114) / 1000;
17 | return result < 128;
18 | }
19 | function prepareIgnoredColor(color) {
20 | if (!color) {
21 | return [];
22 | }
23 | return isRGBArray(color) ? color : [color];
24 | }
25 | function isRGBArray(value) {
26 | return Array.isArray(value[0]);
27 | }
28 | function isIgnoredColor(data, index, ignoredColor) {
29 | for (var i = 0; i < ignoredColor.length; i++) {
30 | if (isIgnoredColorAsNumbers(data, index, ignoredColor[i])) {
31 | return true;
32 | }
33 | }
34 | return false;
35 | }
36 | function isIgnoredColorAsNumbers(data, index, ignoredColor) {
37 | switch (ignoredColor.length) {
38 | case 3:
39 | // [red, green, blue]
40 | if (isIgnoredRGBColor(data, index, ignoredColor)) {
41 | return true;
42 | }
43 | break;
44 | case 4:
45 | // [red, green, blue, alpha]
46 | if (isIgnoredRGBAColor(data, index, ignoredColor)) {
47 | return true;
48 | }
49 | break;
50 | case 5:
51 | // [red, green, blue, alpha, threshold]
52 | if (isIgnoredRGBAColorWithThreshold(data, index, ignoredColor)) {
53 | return true;
54 | }
55 | break;
56 | default:
57 | return false;
58 | }
59 | }
60 | function isIgnoredRGBColor(data, index, ignoredColor) {
61 | // Ignore if the pixel are transparent.
62 | if (data[index + 3] !== 255) {
63 | return true;
64 | }
65 | if (data[index] === ignoredColor[0] &&
66 | data[index + 1] === ignoredColor[1] &&
67 | data[index + 2] === ignoredColor[2]) {
68 | return true;
69 | }
70 | return false;
71 | }
72 | function isIgnoredRGBAColor(data, index, ignoredColor) {
73 | if (data[index + 3] && ignoredColor[3]) {
74 | return data[index] === ignoredColor[0] &&
75 | data[index + 1] === ignoredColor[1] &&
76 | data[index + 2] === ignoredColor[2] &&
77 | data[index + 3] === ignoredColor[3];
78 | }
79 | // Ignore rgb components if the pixel are fully transparent.
80 | return data[index + 3] === ignoredColor[3];
81 | }
82 | function inRange(colorComponent, ignoredColorComponent, value) {
83 | return colorComponent >= (ignoredColorComponent - value) &&
84 | colorComponent <= (ignoredColorComponent + value);
85 | }
86 | function isIgnoredRGBAColorWithThreshold(data, index, ignoredColor) {
87 | var redIgnored = ignoredColor[0];
88 | var greenIgnored = ignoredColor[1];
89 | var blueIgnored = ignoredColor[2];
90 | var alphaIgnored = ignoredColor[3];
91 | var threshold = ignoredColor[4];
92 | var alphaData = data[index + 3];
93 | var alphaInRange = inRange(alphaData, alphaIgnored, threshold);
94 | if (!alphaIgnored) {
95 | return alphaInRange;
96 | }
97 | if (!alphaData && alphaInRange) {
98 | return true;
99 | }
100 | if (inRange(data[index], redIgnored, threshold) &&
101 | inRange(data[index + 1], greenIgnored, threshold) &&
102 | inRange(data[index + 2], blueIgnored, threshold) &&
103 | alphaInRange) {
104 | return true;
105 | }
106 | return false;
107 | }
108 |
109 | var DEFAULT_DOMINANT_DIVIDER = 24;
110 | function dominantAlgorithm(arr, len, options) {
111 | var colorHash = {};
112 | var divider = options.dominantDivider || DEFAULT_DOMINANT_DIVIDER;
113 | var ignoredColor = options.ignoredColor;
114 | var step = options.step;
115 | var max = [0, 0, 0, 0, 0];
116 | for (var i = 0; i < len; i += step) {
117 | var red = arr[i];
118 | var green = arr[i + 1];
119 | var blue = arr[i + 2];
120 | var alpha = arr[i + 3];
121 | if (ignoredColor && isIgnoredColor(arr, i, ignoredColor)) {
122 | continue;
123 | }
124 | var key = Math.round(red / divider) + ',' +
125 | Math.round(green / divider) + ',' +
126 | Math.round(blue / divider);
127 | if (colorHash[key]) {
128 | colorHash[key] = [
129 | colorHash[key][0] + red * alpha,
130 | colorHash[key][1] + green * alpha,
131 | colorHash[key][2] + blue * alpha,
132 | colorHash[key][3] + alpha,
133 | colorHash[key][4] + 1
134 | ];
135 | }
136 | else {
137 | colorHash[key] = [red * alpha, green * alpha, blue * alpha, alpha, 1];
138 | }
139 | if (max[4] < colorHash[key][4]) {
140 | max = colorHash[key];
141 | }
142 | }
143 | var redTotal = max[0];
144 | var greenTotal = max[1];
145 | var blueTotal = max[2];
146 | var alphaTotal = max[3];
147 | var count = max[4];
148 | return alphaTotal ? [
149 | Math.round(redTotal / alphaTotal),
150 | Math.round(greenTotal / alphaTotal),
151 | Math.round(blueTotal / alphaTotal),
152 | Math.round(alphaTotal / count)
153 | ] : options.defaultColor;
154 | }
155 |
156 | function simpleAlgorithm(arr, len, options) {
157 | var redTotal = 0;
158 | var greenTotal = 0;
159 | var blueTotal = 0;
160 | var alphaTotal = 0;
161 | var count = 0;
162 | var ignoredColor = options.ignoredColor;
163 | var step = options.step;
164 | for (var i = 0; i < len; i += step) {
165 | var alpha = arr[i + 3];
166 | var red = arr[i] * alpha;
167 | var green = arr[i + 1] * alpha;
168 | var blue = arr[i + 2] * alpha;
169 | if (ignoredColor && isIgnoredColor(arr, i, ignoredColor)) {
170 | continue;
171 | }
172 | redTotal += red;
173 | greenTotal += green;
174 | blueTotal += blue;
175 | alphaTotal += alpha;
176 | count++;
177 | }
178 | return alphaTotal ? [
179 | Math.round(redTotal / alphaTotal),
180 | Math.round(greenTotal / alphaTotal),
181 | Math.round(blueTotal / alphaTotal),
182 | Math.round(alphaTotal / count)
183 | ] : options.defaultColor;
184 | }
185 |
186 | function sqrtAlgorithm(arr, len, options) {
187 | var redTotal = 0;
188 | var greenTotal = 0;
189 | var blueTotal = 0;
190 | var alphaTotal = 0;
191 | var count = 0;
192 | var ignoredColor = options.ignoredColor;
193 | var step = options.step;
194 | for (var i = 0; i < len; i += step) {
195 | var red = arr[i];
196 | var green = arr[i + 1];
197 | var blue = arr[i + 2];
198 | var alpha = arr[i + 3];
199 | if (ignoredColor && isIgnoredColor(arr, i, ignoredColor)) {
200 | continue;
201 | }
202 | redTotal += red * red * alpha;
203 | greenTotal += green * green * alpha;
204 | blueTotal += blue * blue * alpha;
205 | alphaTotal += alpha;
206 | count++;
207 | }
208 | return alphaTotal ? [
209 | Math.round(Math.sqrt(redTotal / alphaTotal)),
210 | Math.round(Math.sqrt(greenTotal / alphaTotal)),
211 | Math.round(Math.sqrt(blueTotal / alphaTotal)),
212 | Math.round(alphaTotal / count)
213 | ] : options.defaultColor;
214 | }
215 |
216 | function getDefaultColor(options) {
217 | return getOption(options, 'defaultColor', [0, 0, 0, 0]);
218 | }
219 | function getOption(options, name, defaultValue) {
220 | return (options[name] === undefined ? defaultValue : options[name]);
221 | }
222 |
223 | var MIN_SIZE = 10;
224 | var MAX_SIZE = 100;
225 | function isSvg(filename) {
226 | return filename.search(/\.svg(\?|$)/i) !== -1;
227 | }
228 | function getOriginalSize(resource) {
229 | if (isInstanceOfHTMLImageElement(resource)) {
230 | var width = resource.naturalWidth;
231 | var height = resource.naturalHeight;
232 | // For SVG images with only viewBox attribute
233 | if (!resource.naturalWidth && isSvg(resource.src)) {
234 | width = height = MAX_SIZE;
235 | }
236 | return {
237 | width: width,
238 | height: height,
239 | };
240 | }
241 | if (isInstanceOfHTMLVideoElement(resource)) {
242 | return {
243 | width: resource.videoWidth,
244 | height: resource.videoHeight
245 | };
246 | }
247 | if (isInstanceOfVideoFrame(resource)) {
248 | return {
249 | width: resource.codedWidth,
250 | height: resource.codedHeight,
251 | };
252 | }
253 | return {
254 | width: resource.width,
255 | height: resource.height
256 | };
257 | }
258 | function getSrc(resource) {
259 | if (isInstanceOfHTMLCanvasElement(resource)) {
260 | return 'canvas';
261 | }
262 | if (isInstanceOfOffscreenCanvas(resource)) {
263 | return 'offscreencanvas';
264 | }
265 | if (isInstanceOfVideoFrame(resource)) {
266 | return 'videoframe';
267 | }
268 | if (isInstanceOfImageBitmap(resource)) {
269 | return 'imagebitmap';
270 | }
271 | return resource.src;
272 | }
273 | function isInstanceOfHTMLImageElement(resource) {
274 | return typeof HTMLImageElement !== 'undefined' && resource instanceof HTMLImageElement;
275 | }
276 | var hasOffscreenCanvas = typeof OffscreenCanvas !== 'undefined';
277 | function isInstanceOfOffscreenCanvas(resource) {
278 | return hasOffscreenCanvas && resource instanceof OffscreenCanvas;
279 | }
280 | function isInstanceOfHTMLVideoElement(resource) {
281 | return typeof HTMLVideoElement !== 'undefined' && resource instanceof HTMLVideoElement;
282 | }
283 | function isInstanceOfVideoFrame(resource) {
284 | return typeof VideoFrame !== 'undefined' && resource instanceof VideoFrame;
285 | }
286 | function isInstanceOfHTMLCanvasElement(resource) {
287 | return typeof HTMLCanvasElement !== 'undefined' && resource instanceof HTMLCanvasElement;
288 | }
289 | function isInstanceOfImageBitmap(resource) {
290 | return typeof ImageBitmap !== 'undefined' && resource instanceof ImageBitmap;
291 | }
292 | function prepareSizeAndPosition(originalSize, options) {
293 | var srcLeft = getOption(options, 'left', 0);
294 | var srcTop = getOption(options, 'top', 0);
295 | var srcWidth = getOption(options, 'width', originalSize.width);
296 | var srcHeight = getOption(options, 'height', originalSize.height);
297 | var destWidth = srcWidth;
298 | var destHeight = srcHeight;
299 | if (options.mode === 'precision') {
300 | return {
301 | srcLeft: srcLeft,
302 | srcTop: srcTop,
303 | srcWidth: srcWidth,
304 | srcHeight: srcHeight,
305 | destWidth: destWidth,
306 | destHeight: destHeight
307 | };
308 | }
309 | var factor;
310 | if (srcWidth > srcHeight) {
311 | factor = srcWidth / srcHeight;
312 | destWidth = MAX_SIZE;
313 | destHeight = Math.round(destWidth / factor);
314 | }
315 | else {
316 | factor = srcHeight / srcWidth;
317 | destHeight = MAX_SIZE;
318 | destWidth = Math.round(destHeight / factor);
319 | }
320 | if (destWidth > srcWidth || destHeight > srcHeight ||
321 | destWidth < MIN_SIZE || destHeight < MIN_SIZE) {
322 | destWidth = srcWidth;
323 | destHeight = srcHeight;
324 | }
325 | return {
326 | srcLeft: srcLeft,
327 | srcTop: srcTop,
328 | srcWidth: srcWidth,
329 | srcHeight: srcHeight,
330 | destWidth: destWidth,
331 | destHeight: destHeight
332 | };
333 | }
334 | var isWebWorkers = typeof window === 'undefined';
335 | function makeCanvas() {
336 | if (isWebWorkers) {
337 | return hasOffscreenCanvas ? new OffscreenCanvas(1, 1) : null;
338 | }
339 | return document.createElement('canvas');
340 | }
341 |
342 | var ERROR_PREFIX = 'FastAverageColor: ';
343 | function getError(message) {
344 | return Error(ERROR_PREFIX + message);
345 | }
346 | function outputError(error, silent) {
347 | if (!silent) {
348 | console.error(error);
349 | }
350 | }
351 |
352 | var FastAverageColor = /** @class */ (function () {
353 | function FastAverageColor() {
354 | this.canvas = null;
355 | this.ctx = null;
356 | }
357 | FastAverageColor.prototype.getColorAsync = function (resource, options) {
358 | if (!resource) {
359 | return Promise.reject(getError('call .getColorAsync() without resource'));
360 | }
361 | if (typeof resource === 'string') {
362 | // Web workers
363 | if (typeof Image === 'undefined') {
364 | return Promise.reject(getError('resource as string is not supported in this environment'));
365 | }
366 | var img = new Image();
367 | img.crossOrigin = options && options.crossOrigin || '';
368 | img.src = resource;
369 | return this.bindImageEvents(img, options);
370 | }
371 | else if (isInstanceOfHTMLImageElement(resource) && !resource.complete) {
372 | return this.bindImageEvents(resource, options);
373 | }
374 | else {
375 | var result = this.getColor(resource, options);
376 | return result.error ? Promise.reject(result.error) : Promise.resolve(result);
377 | }
378 | };
379 | /**
380 | * Get the average color from images, videos and canvas.
381 | */
382 | FastAverageColor.prototype.getColor = function (resource, options) {
383 | options = options || {};
384 | var defaultColor = getDefaultColor(options);
385 | if (!resource) {
386 | var error = getError('call .getColor() without resource');
387 | outputError(error, options.silent);
388 | return this.prepareResult(defaultColor, error);
389 | }
390 | var originalSize = getOriginalSize(resource);
391 | var size = prepareSizeAndPosition(originalSize, options);
392 | if (!size.srcWidth || !size.srcHeight || !size.destWidth || !size.destHeight) {
393 | var error = getError("incorrect sizes for resource \"".concat(getSrc(resource), "\""));
394 | outputError(error, options.silent);
395 | return this.prepareResult(defaultColor, error);
396 | }
397 | if (!this.canvas) {
398 | this.canvas = makeCanvas();
399 | if (!this.canvas) {
400 | var error = getError('OffscreenCanvas is not supported in this browser');
401 | outputError(error, options.silent);
402 | return this.prepareResult(defaultColor, error);
403 | }
404 | }
405 | if (!this.ctx) {
406 | this.ctx = this.canvas.getContext('2d', { willReadFrequently: true });
407 | if (!this.ctx) {
408 | var error = getError('Canvas Context 2D is not supported in this browser');
409 | outputError(error, options.silent);
410 | return this.prepareResult(defaultColor);
411 | }
412 | this.ctx.imageSmoothingEnabled = false;
413 | }
414 | this.canvas.width = size.destWidth;
415 | this.canvas.height = size.destHeight;
416 | try {
417 | this.ctx.clearRect(0, 0, size.destWidth, size.destHeight);
418 | this.ctx.drawImage(resource, size.srcLeft, size.srcTop, size.srcWidth, size.srcHeight, 0, 0, size.destWidth, size.destHeight);
419 | var bitmapData = this.ctx.getImageData(0, 0, size.destWidth, size.destHeight).data;
420 | return this.prepareResult(this.getColorFromArray4(bitmapData, options));
421 | }
422 | catch (originalError) {
423 | var error = getError("security error (CORS) for resource ".concat(getSrc(resource), ".\nDetails: https://developer.mozilla.org/en/docs/Web/HTML/CORS_enabled_image"));
424 | outputError(error, options.silent);
425 | if (!options.silent) {
426 | console.error(originalError);
427 | }
428 | return this.prepareResult(defaultColor, error);
429 | }
430 | };
431 | /**
432 | * Get the average color from a array when 1 pixel is 4 bytes.
433 | */
434 | FastAverageColor.prototype.getColorFromArray4 = function (arr, options) {
435 | options = options || {};
436 | var bytesPerPixel = 4;
437 | var arrLength = arr.length;
438 | var defaultColor = getDefaultColor(options);
439 | if (arrLength < bytesPerPixel) {
440 | return defaultColor;
441 | }
442 | var len = arrLength - arrLength % bytesPerPixel;
443 | var step = (options.step || 1) * bytesPerPixel;
444 | var algorithm;
445 | switch (options.algorithm || 'sqrt') {
446 | case 'simple':
447 | algorithm = simpleAlgorithm;
448 | break;
449 | case 'sqrt':
450 | algorithm = sqrtAlgorithm;
451 | break;
452 | case 'dominant':
453 | algorithm = dominantAlgorithm;
454 | break;
455 | default:
456 | throw getError("".concat(options.algorithm, " is unknown algorithm"));
457 | }
458 | return algorithm(arr, len, {
459 | defaultColor: defaultColor,
460 | ignoredColor: prepareIgnoredColor(options.ignoredColor),
461 | step: step,
462 | dominantDivider: options.dominantDivider,
463 | });
464 | };
465 | /**
466 | * Get color data from value ([r, g, b, a]).
467 | */
468 | FastAverageColor.prototype.prepareResult = function (value, error) {
469 | var rgb = value.slice(0, 3);
470 | var rgba = [value[0], value[1], value[2], value[3] / 255];
471 | var isDarkColor = isDark(value);
472 | return {
473 | value: [value[0], value[1], value[2], value[3]],
474 | rgb: 'rgb(' + rgb.join(',') + ')',
475 | rgba: 'rgba(' + rgba.join(',') + ')',
476 | hex: arrayToHex(rgb),
477 | hexa: arrayToHex(value),
478 | isDark: isDarkColor,
479 | isLight: !isDarkColor,
480 | error: error,
481 | };
482 | };
483 | /**
484 | * Destroy the instance.
485 | */
486 | FastAverageColor.prototype.destroy = function () {
487 | if (this.canvas) {
488 | this.canvas.width = 1;
489 | this.canvas.height = 1;
490 | this.canvas = null;
491 | }
492 | this.ctx = null;
493 | };
494 | FastAverageColor.prototype.bindImageEvents = function (resource, options) {
495 | var _this = this;
496 | return new Promise(function (resolve, reject) {
497 | var onload = function () {
498 | unbindEvents();
499 | var result = _this.getColor(resource, options);
500 | if (result.error) {
501 | reject(result.error);
502 | }
503 | else {
504 | resolve(result);
505 | }
506 | };
507 | var onerror = function () {
508 | unbindEvents();
509 | reject(getError("Error loading image \"".concat(resource.src, "\"")));
510 | };
511 | var onabort = function () {
512 | unbindEvents();
513 | reject(getError("Image \"".concat(resource.src, "\" loading aborted")));
514 | };
515 | var unbindEvents = function () {
516 | resource.removeEventListener('load', onload);
517 | resource.removeEventListener('error', onerror);
518 | resource.removeEventListener('abort', onabort);
519 | };
520 | resource.addEventListener('load', onload);
521 | resource.addEventListener('error', onerror);
522 | resource.addEventListener('abort', onabort);
523 | });
524 | };
525 | return FastAverageColor;
526 | }());
527 |
528 | var hasDocument = typeof document !== 'undefined';
529 | var hasWindow = typeof window !== 'undefined';
530 | var hasNavigator = typeof navigator != 'undefined';
531 | var hasScreen = typeof screen != 'undefined';
532 | function getCharset() {
533 | return hasDocument && typeof document.charset === 'string' ?
534 | document.charset.toLowerCase() :
535 | '';
536 | }
537 | function getPageUrl() {
538 | return hasWindow && window.location ? window.location.href : '';
539 | }
540 | function getReferrer() {
541 | return hasDocument ? document.referrer : '';
542 | }
543 | function getTitle() {
544 | return hasDocument ? document.title : '';
545 | }
546 | function cookieEnabled() {
547 | return hasNavigator ? navigator.cookieEnabled : false;
548 | }
549 | function getScreenSize() {
550 | return hasScreen ? [
551 | screen.width,
552 | screen.height,
553 | screen.colorDepth
554 | ].join('x') : '';
555 | }
556 | var DEFAULT_DEVICE_PIXEL_RATIO = 1;
557 | function getDevicePixelRatio() {
558 | return hasWindow ?
559 | (window.devicePixelRatio || DEFAULT_DEVICE_PIXEL_RATIO) :
560 | DEFAULT_DEVICE_PIXEL_RATIO;
561 | }
562 | function getClientSize() {
563 | return hasWindow ? [
564 | window.innerWidth,
565 | window.innerHeight
566 | ].join('x') : '';
567 | }
568 |
569 | function truncate(str, len) {
570 | return (str || '').slice(0, len);
571 | }
572 |
573 | function getRandom() {
574 | return Math.floor(Math.random() * (1 << 31 - 1));
575 | }
576 |
577 | function getSeconds() {
578 | return Math.round(Date.now() / 1000);
579 | }
580 |
581 | var MAX_TITLE_LEN = 512;
582 | function addParam(result, name, value) {
583 | if (value || value === 0) {
584 | result.push(name + ':' + (value === true ? '1' : value));
585 | }
586 | }
587 | function getBrowserInfo(params, title) {
588 | var result = [];
589 | if (params) {
590 | Object.keys(params).forEach(function (key) { return addParam(result, key, params[key]); });
591 | }
592 | addParam(result, 'rn', getRandom());
593 | addParam(result, 'c', cookieEnabled());
594 | addParam(result, 's', getScreenSize());
595 | addParam(result, 'sk', getDevicePixelRatio());
596 | addParam(result, 'w', getClientSize());
597 | addParam(result, 'en', getCharset());
598 | var time = getSeconds();
599 | addParam(result, 'et', time);
600 | addParam(result, 'st', time);
601 | addParam(result, 't', truncate(title, MAX_TITLE_LEN));
602 | return result.join(':');
603 | }
604 |
605 | function queryStringify(params) {
606 | return Object.keys(params)
607 | .filter(function (key) { return params[key] || params[key] === 0; })
608 | .map(function (key) { return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]); })
609 | .join('&');
610 | }
611 | var MAX_URL_LEN = 1024;
612 | function prepareUrl(url) {
613 | return truncate(url, MAX_URL_LEN);
614 | }
615 |
616 | var metrikaOrigin = 'https://mc.yandex.ru';
617 | function sendData(counterId, queryParams) {
618 | var url = metrikaOrigin + '/watch/' + counterId + '?' + queryStringify(queryParams);
619 | var hasBeacon = typeof navigator !== 'undefined' && navigator.sendBeacon;
620 | if (!hasBeacon || !navigator.sendBeacon(url, ' ')) {
621 | if (typeof fetch !== 'undefined') {
622 | fetch(url, { credentials: 'include' }).catch(function () { });
623 | }
624 | else if (typeof Image !== 'undefined') {
625 | new Image().src = url;
626 | }
627 | }
628 | }
629 |
630 | function hitExt(hitExtParams) {
631 | var browserInfo = hitExtParams.browserInfo, counterId = hitExtParams.counterId, pageParams = hitExtParams.pageParams;
632 | var data = {
633 | 'browser-info': getBrowserInfo(browserInfo, pageParams.title),
634 | rn: getRandom(),
635 | ut: pageParams.ut
636 | };
637 | if (pageParams.url) {
638 | data['page-url'] = prepareUrl(pageParams.url);
639 | }
640 | if (pageParams.referrer) {
641 | data['page-ref'] = prepareUrl(pageParams.referrer);
642 | }
643 | sendData(counterId, data);
644 | }
645 | /**
646 | * Отправка хита.
647 | *
648 | * @param counterId - Номер счётчика.
649 | * @param hitParams - Параметры страницы.
650 | * @param userVars - Параметры визитов.
651 | *
652 | * @example
653 | * hit('123456');
654 | *
655 | * hit('123456', {
656 | * referer: document.referer,
657 | * title: document.title,
658 | * url: window.location.href
659 | * }, {
660 | * myParam: 'value'
661 | * });
662 | */
663 | function hit(counterId, hitParams, params) {
664 | var referrer = getReferrer();
665 | var title = getTitle();
666 | var url = getPageUrl();
667 | hitExt({
668 | browserInfo: { pv: true, ar: true },
669 | counterId: counterId,
670 | pageParams: {
671 | referrer: referrer,
672 | title: title,
673 | url: url
674 | }});
675 | }
676 |
677 | window.addEventListener('load', function () {
678 | var pages = [
679 | 'background',
680 | 'timeline',
681 | 'gradient',
682 | 'gradient_stripes',
683 | 'border',
684 | 'gallery',
685 | 'gallery_vertical',
686 | 'box-shadow',
687 | 'box-shadow-4-sides',
688 | 'ambilight',
689 | 'text-photo',
690 | 'canvas',
691 | 'define'
692 | ];
693 | var prev = pages[pages.length - 1];
694 | var next = pages[1];
695 | pages.some(function (item, i) {
696 | prev = pages[i - 1] || pages[pages.length - 1];
697 | next = pages[i + 1] || pages[0];
698 | return location.pathname.search('/' + item + '\\.') > -1;
699 | });
700 | var nav = document.createElement('div');
701 | nav.innerHTML = '\
702 |
🏠\
703 |
◀\
704 |
▶\
705 |
';
706 | document.body.appendChild(nav);
707 | hit('49603183');
708 | }, false);
709 |
710 | var fac = new FastAverageColor();
711 | window.addEventListener('load', function () {
712 | Array.from(document.querySelectorAll('.item')).forEach(function (item) {
713 | var color = fac.getColor(item.querySelector('img'));
714 | item.style.backgroundColor = color.rgb;
715 | item.style.color = color.isDark ? 'white' : 'black';
716 | });
717 | }, false);
718 |
719 | }));
720 |
--------------------------------------------------------------------------------
/dist/background_async.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
58 | img
59 | {
60 | width: 100%;
61 | }
62 |
63 | .item
64 | {
65 | float: left;
66 |
67 | box-sizing: border-box;
68 | width: 500px;
69 | max-width: 100%;
70 | padding: 50px 50px 45px 50px;
71 |
72 | transition: background-color .5s ease;
73 | }
74 |
75 | .item__text
76 | {
77 | padding-top: 10px;
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/dist/background_async_dominant.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
58 | img
59 | {
60 | width: 100%;
61 | }
62 |
63 | .item
64 | {
65 | float: left;
66 |
67 | box-sizing: border-box;
68 | width: 500px;
69 | max-width: 100%;
70 | padding: 50px 50px 45px 50px;
71 |
72 | transition: background-color .5s ease;
73 | }
74 |
75 | .item__text
76 | {
77 | padding-top: 10px;
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/dist/border.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
58 | .item
59 | {
60 | float: left;
61 |
62 | box-sizing: border-box;
63 | width: 400px;
64 | max-width: 100%;
65 | padding: 30px;
66 |
67 | transition: border-color .5s ease;
68 | }
69 |
70 | .item__image-container
71 | {
72 | transition: all .3s ease;
73 |
74 | border: 40px solid #000;
75 | outline: 1px solid rgba(255, 255, 255, .08);
76 | }
77 |
78 | .item__image
79 | {
80 | display: block;
81 |
82 | width: 100%;
83 |
84 | outline: 1px solid rgba(255, 255, 255, .08);
85 | }
86 |
87 | .item__text
88 | {
89 | position: relative;
90 | z-index: 10;
91 |
92 | padding: 10px;
93 |
94 | color: #fff;
95 | }
96 |
97 |
--------------------------------------------------------------------------------
/dist/box-shadow-4-sides.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
58 | img
59 | {
60 | width: 100%;
61 |
62 | transition: all .3s ease;
63 |
64 | border-radius: 50px;
65 | }
66 |
67 | .item
68 | {
69 | float: left;
70 |
71 | box-sizing: border-box;
72 | width: 540px;
73 | max-width: 100%;
74 | padding: 70px;
75 |
76 | transition: background-color .5s ease;
77 | }
78 |
79 | .item__text
80 | {
81 | position: relative;
82 | z-index: 10;
83 |
84 | padding: 10px;
85 | }
86 |
87 |
--------------------------------------------------------------------------------
/dist/box-shadow.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
58 | img
59 | {
60 | width: 100%;
61 |
62 | transition: all .3s ease;
63 |
64 | border-radius: 50px;
65 | }
66 |
67 | img:hover
68 | {
69 | transform: translateY(10px);
70 |
71 | opacity: .8;
72 | }
73 |
74 | .item
75 | {
76 | float: left;
77 |
78 | box-sizing: border-box;
79 | width: 540px;
80 | max-width: 100%;
81 | padding: 30px 50px;
82 |
83 | transition: background-color .5s ease;
84 | }
85 |
86 | .item__text
87 | {
88 | position: relative;
89 | z-index: 10;
90 |
91 | padding: 10px;
92 | }
93 |
94 |
--------------------------------------------------------------------------------
/dist/canvas.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
58 | canvas
59 | {
60 | position: fixed;
61 | top: 50%;
62 | left: 50%;
63 |
64 | transform: translate(-50%, -50%);
65 |
66 | border: 1px solid rgba(100, 100, 100, .5);
67 | }
68 |
69 | .info
70 | {
71 | font-family: Arial;
72 | font-size: 16px;
73 |
74 | position: fixed;
75 | z-index: 10;
76 | top: 50px;
77 | left: 50px;
78 |
79 | padding: 20px;
80 |
81 | color: white;
82 | border-radius: 10px;
83 | background: rgba(255, 255, 255, .1);
84 | text-shadow: 1px 1px 2px black;
85 | }
86 |
87 | .info__item
88 | {
89 | overflow-x: hidden;
90 |
91 | width: 250px;
92 |
93 | white-space: nowrap;
94 | text-overflow: ellipsis;
95 | }
96 |
97 | .info__item, .option {
98 | padding: 3px;
99 | }
100 |
101 | .option {
102 | margin: 5px 0 5px 0;
103 | }
104 |
105 | #start {
106 | font-size: 14px;
107 | padding: 10px;
108 | width: 100%;
109 | box-sizing: border-box;
110 | }
111 |
112 |
--------------------------------------------------------------------------------
/dist/define.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
58 | .select-file-container
59 | {
60 | margin-top: 50px;
61 | padding: 10px;
62 |
63 | text-align: center;
64 | white-space: nowrap;
65 |
66 | color: white;
67 | background: rgba(0, 0, 0, .5);
68 | }
69 |
70 | .images
71 | {
72 | margin-top: 50px;
73 | }
74 |
75 | .images__item
76 | {
77 | margin: 0 25px 25px 25px;
78 |
79 | transition: all .3s linear;
80 | display: inline-block;
81 |
82 | border-radius: 5px;
83 | }
84 |
85 | .images__img-container
86 | {
87 | padding: 25px;
88 | }
89 |
90 | .images__img
91 | {
92 | max-width: 100%;
93 |
94 | box-sizing: border-box;
95 | border: 1px solid gray;
96 | background: url('../images/transparent.png') 0 0 repeat;
97 | }
98 |
99 | .images__title
100 | {
101 | width: 100%;
102 | padding: 10px;
103 | border-bottom: 1px solid rgba(255, 255, 255, .1);
104 | box-sizing: border-box;
105 |
106 | text-overflow: ellipsis;
107 | overflow-x: hidden;
108 | }
109 |
110 | .images__body
111 | {
112 | padding: 10px;
113 | border-bottom: 1px solid rgba(255, 255, 255, .1);
114 | box-sizing: border-box;
115 | }
116 |
117 | .images__algorithm {
118 | padding: 5px;
119 | display: block;
120 | background: url('../images/transparent.png') 0 0 repeat;
121 | border-radius: 5px;
122 | }
123 |
124 |
--------------------------------------------------------------------------------
/dist/gallery.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
58 | .big-image {
59 | position: fixed;
60 |
61 | left: 50%;
62 | top: 50%;
63 | max-width: 60%;
64 | width: 640px;
65 | height: auto;
66 |
67 | transform: translate(-50%, -50%);
68 |
69 | transition: opacity 1s ease;
70 | opacity: 1;
71 | }
72 |
73 | .big-image_hidden {
74 | opacity: 0;
75 | }
76 |
77 | .big-image-border {
78 | --width: calc(100vw / 2 - 640px / 2 + .5px);
79 | --height: calc(100vh / 2 - 480px / 2 + .5px);
80 |
81 | position: fixed;
82 |
83 | left: 0;
84 | top: 0;
85 | width: 100%;
86 | height: 100%;
87 | box-sizing: border-box;
88 |
89 | border-left: var(--width) solid transparent;
90 | border-right: var(--width) solid transparent;
91 | border-top: var(--height) solid transparent;
92 | border-bottom: var(--height) solid transparent;
93 |
94 | transition: border-color .5s ease;
95 | }
96 |
97 | @media (max-width: 1066px) {
98 | .big-image-border {
99 | outline: 10px solid red;
100 | --width: calc(100vw / 2 - 60vw / 2 + .5px);
101 | --height: calc(100vh / 2 - 45vw / 2 + .5px);
102 | }
103 | }
104 |
105 | .slider {
106 | position: fixed;
107 |
108 | left: 50%;
109 | bottom: 0;
110 |
111 | transform: translateX(-50%);
112 |
113 | white-space: nowrap;
114 |
115 | background: rgba(255, 255, 255, .2);
116 |
117 | border-radius: 5px 5px 0 0;
118 |
119 | line-height: 0;
120 | }
121 |
122 | .slider__item {
123 | border: 0;
124 |
125 | margin: 5px 5px 5px 0;
126 | width: 80px;
127 | height: 60px;
128 |
129 | cursor: pointer;
130 |
131 | transition: opacity .3s ease;
132 | }
133 |
134 | .slider__item_active {
135 | outline: 5px solid orange;
136 | }
137 |
138 | .slider__item_first {
139 | margin-left: 5px;
140 | }
141 |
142 | .slider__item_last {
143 | border-radius: 0 5px 0 0;
144 | }
145 |
146 | .slider__item:hover {
147 | opacity: .6;
148 | }
149 |
150 | .slider__item_active:hover {
151 | opacity: 1;
152 | }
153 |
154 |
--------------------------------------------------------------------------------
/dist/gallery_vertical.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
58 | .big-image-container {
59 | position: fixed;
60 |
61 | left: 50%;
62 | top: 50%;
63 | width: 640px;
64 | height: auto;
65 |
66 | transform: translate(-50%, -50%);
67 |
68 | transition: opacity 1s ease;
69 | opacity: 1;
70 | }
71 |
72 | .big-image {
73 | display: block;
74 | width: 100%;
75 | height: auto;
76 | }
77 |
78 | .vertical-gradient {
79 | position: absolute;
80 |
81 | left: 0;
82 | top: 0;
83 | width: 50px;
84 | height: 100%;
85 | margin-left: -60px;
86 | }
87 |
88 | .horizontal-gradient {
89 | position: absolute;
90 |
91 | left: 0;
92 | top: 0;
93 | width: 100%;
94 | height: 50px;
95 | margin-top: -60px;
96 | }
97 |
98 | .big-image_hidden {
99 | opacity: 0;
100 | }
101 |
102 | .slider {
103 | position: fixed;
104 |
105 | left: 50%;
106 | bottom: 0;
107 |
108 | transform: translateX(-50%);
109 |
110 | white-space: nowrap;
111 |
112 | background: rgba(255, 255, 255, .2);
113 |
114 | border-radius: 5px 5px 0 0;
115 |
116 | line-height: 0;
117 | }
118 |
119 | .slider__item {
120 | border: 0;
121 |
122 | margin: 5px 5px 5px 0;
123 | width: 80px;
124 | height: 60px;
125 |
126 | cursor: pointer;
127 |
128 | transition: opacity .3s ease;
129 | }
130 |
131 | .slider__item_active {
132 | outline: 5px solid orange;
133 | }
134 |
135 | .slider__item_first {
136 | margin-left: 5px;
137 | }
138 |
139 | .slider__item_last {
140 | border-radius: 0 5px 0 0;
141 | }
142 |
143 | .slider__item:hover {
144 | opacity: .6;
145 | }
146 |
147 | .slider__item_active:hover {
148 | opacity: 1;
149 | }
150 |
151 |
--------------------------------------------------------------------------------
/dist/gradient.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
58 | .item
59 | {
60 | position: relative;
61 |
62 | float: left;
63 |
64 | box-sizing: border-box;
65 | width: 400px;
66 | max-width: 100%;
67 | margin: 1px 1px 0 0;
68 |
69 | transition: all .5s ease;
70 | }
71 |
72 | .item__image
73 | {
74 | position: relative;
75 | }
76 |
77 | img
78 | {
79 | display: block;
80 |
81 | width: 100%;
82 | }
83 |
84 | .item__gradient
85 | {
86 | position: absolute;
87 | z-index: 1;
88 | top: 0;
89 | right: 0;
90 | bottom: 50%;
91 | left: 0;
92 |
93 | transition: all .5s ease;
94 | }
95 |
96 | .item_bottom .item__gradient
97 | {
98 | top: 50%;
99 | bottom: 0;
100 | }
101 |
102 |
103 | .item__text
104 | {
105 | padding: 20px;
106 | }
107 |
108 |
--------------------------------------------------------------------------------
/dist/gradient_stripes.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
58 | .items
59 | {
60 | box-sizing: border-box;
61 | padding: 20px;
62 | }
63 |
64 | .item
65 | {
66 | position: relative;
67 |
68 | float: left;
69 |
70 | box-sizing: border-box;
71 | width: 460px;
72 | max-width: 100%;
73 | margin: 0 20px 20px 0;
74 | padding: 30px;
75 |
76 | transition: all .5s ease;
77 | }
78 |
79 | img
80 | {
81 | display: block;
82 |
83 | width: 100%;
84 | }
85 |
86 | .item__text
87 | {
88 | padding: 10px 0 10px 0;
89 | }
90 |
91 |
--------------------------------------------------------------------------------
/dist/text-photo.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
58 | .controls
59 | {
60 | font-size: 11px;
61 | line-height: 14px;
62 |
63 | position: fixed;
64 | z-index: 100;
65 | top: 70px;
66 | left: 10px;
67 |
68 | margin: 0;
69 | padding: 0;
70 |
71 | color: #999;
72 | }
73 |
74 | .controls label
75 | {
76 | display: block;
77 | }
78 |
79 | .controls input
80 | {
81 | font-size: 11px;
82 | line-height: 14px;
83 | }
84 |
85 | .big-photo,
86 | .text-photo
87 | {
88 | position: fixed;
89 | top: 50%;
90 | left: 50%;
91 |
92 | transform: translate(-50%, -50%);
93 | }
94 |
95 | @media (max-width: 900px) and (min-width: 800px)
96 | {
97 | .big-photo,
98 | .text-photo
99 | {
100 | transform: translate(-50%, -50%) scale(.9);
101 | }
102 | }
103 |
104 | @media (max-width: 800px) and (min-width: 700px)
105 | {
106 | .big-photo,
107 | .text-photo
108 | {
109 | transform: translate(-50%, -50%) scale(.8);
110 | }
111 | }
112 |
113 | @media (max-width: 700px) and (min-width: 600px)
114 | {
115 | .big-photo,
116 | .text-photo
117 | {
118 | transform: translate(-50%, -50%) scale(.7);
119 | }
120 | }
121 |
122 | @media (max-width: 600px)
123 | {
124 | .big-photo,
125 | .text-photo
126 | {
127 | transform: translate(-50%, -50%) scale(.6);
128 | }
129 | }
130 |
131 | .text-photo
132 | {
133 | visibility: visible;
134 |
135 | opacity: 0;
136 | }
137 |
138 | .big-photo_fade
139 | {
140 | transition: opacity 2s ease;
141 |
142 | opacity: 0;
143 | }
144 |
145 | .big-photo_load
146 | {
147 | visibility: hidden;
148 | }
149 |
150 | .text-photo_fade
151 | {
152 | transition: opacity 2s ease;
153 |
154 | opacity: 1;
155 | }
156 |
157 |
--------------------------------------------------------------------------------
/dist/timeline.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
58 | body {
59 | background: #000;
60 | color: #fff;
61 |
62 | padding: 10px;
63 | }
64 |
65 | .title-container {
66 | position: absolute;
67 | left: 0;
68 | right: 0;
69 | top: 10px;
70 | text-align: center;
71 | }
72 |
73 | .title {
74 | display: inline-block;
75 | margin: 0 auto;
76 | padding: 0;
77 | color: #fff;
78 | font-size: 30px;
79 | font-weight: normal;
80 | line-height: 30px;
81 | background: linear-gradient(90deg, #555, #fff);
82 | background-clip: text;
83 | -webkit-background-clip: text;
84 | -webkit-text-fill-color: transparent;
85 | white-space: nowrap;
86 | }
87 |
88 | .spinner,
89 | .spinner:after {
90 | border-radius: 50%;
91 | width: 50px;
92 | height: 50px;
93 | }
94 |
95 | .spinner {
96 | position: fixed;
97 | left: 50%;
98 | top: 50%;
99 | transform: translate(-50%, -50%);
100 | border-top: 1.1em solid rgba(255, 255, 255, 0.2);
101 | border-right: 1.1em solid rgba(255, 255, 255, 0.2);
102 | border-bottom: 1.1em solid rgba(255, 255, 255, 0.2);
103 | border-left: 1.1em solid #ffffff;
104 | transform: translateZ(0);
105 | animation: spinner 1.1s infinite linear;
106 | }
107 |
108 | @keyframes spinner {
109 | 0% {
110 | transform: rotate(0deg);
111 | }
112 | 100% {
113 | transform: rotate(360deg);
114 | }
115 | }
116 |
117 | .timeline {
118 | margin: 0 0 10px 0;
119 | }
120 |
121 | .timeline__label {
122 | font-size: 12px;
123 | color: #ccc;
124 | }
125 |
126 | .timeline__colors {
127 | width: auto;
128 | line-height: 0;
129 | font-size: 0;
130 | border: 2px solid rgba(255,255,255,0.1);
131 | }
132 |
133 | .timeline__color {
134 | display: inline-block;
135 | width: 1px;
136 | height: 50px;
137 | }
138 |
139 | .progress {
140 | position: fixed;
141 | top: 10px;
142 | right: 10px;
143 | text-align: right;
144 | }
145 |
146 | .demo {
147 | text-align: center;
148 | }
149 |
150 | .radial-demo,
151 | .conic-demo {
152 | display: inline-block;
153 | width: 48vw;
154 | height: 48vw;
155 | margin-right: 10px;
156 | }
157 |
158 | video {
159 | position: fixed;
160 | right: 10px;
161 | bottom: 10px;
162 | border-radius: 10px;
163 | max-width: 400px;
164 | }
165 |
166 | .movie-selector {
167 | margin-top: 50px;
168 | margin-bottom: 10px;
169 | }
170 |
171 |
--------------------------------------------------------------------------------
/gallery.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Get average color for gallery
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/gallery_vertical.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Get vertical or horizontal average color for gallery
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |

15 |
16 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/gradient.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Average color: Gradient
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |

14 |
15 |
16 |
17 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
18 |
19 |
20 |
21 |
22 |
23 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
24 |
25 |
26 |

27 |
28 |
29 |
30 |
31 |
32 |
33 |

34 |
35 |
36 |
37 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
38 |
39 |
40 |
41 |
42 |
43 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
44 |
45 |
46 |

47 |
48 |
49 |
50 |
51 |
52 |
53 |

54 |
55 |
56 |
57 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
58 |
59 |
60 |
61 |
62 |
63 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
64 |
65 |
66 |

67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/gradient_stripes.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Get average color for Minecraft gradient
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |

14 |
15 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
16 |
17 |
18 |
19 |
20 |

21 |
22 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
23 |
24 |
25 |
26 |
27 |

28 |
29 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
30 |
31 |
32 |
33 |
34 |

35 |
36 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
37 |
38 |
39 |
40 |
41 |

42 |
43 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
44 |
45 |
46 |
47 |
48 |

49 |
50 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/images/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/1.jpg
--------------------------------------------------------------------------------
/images/10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/10.jpg
--------------------------------------------------------------------------------
/images/11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/11.jpg
--------------------------------------------------------------------------------
/images/12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/12.jpg
--------------------------------------------------------------------------------
/images/13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/13.jpg
--------------------------------------------------------------------------------
/images/14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/14.jpg
--------------------------------------------------------------------------------
/images/15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/15.jpg
--------------------------------------------------------------------------------
/images/16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/16.jpg
--------------------------------------------------------------------------------
/images/17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/17.jpg
--------------------------------------------------------------------------------
/images/18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/18.jpg
--------------------------------------------------------------------------------
/images/19.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/19.jpg
--------------------------------------------------------------------------------
/images/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/2.jpg
--------------------------------------------------------------------------------
/images/20.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/20.jpg
--------------------------------------------------------------------------------
/images/21.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/21.jpg
--------------------------------------------------------------------------------
/images/22.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/22.jpg
--------------------------------------------------------------------------------
/images/23.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/23.jpg
--------------------------------------------------------------------------------
/images/24.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/24.jpg
--------------------------------------------------------------------------------
/images/25.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/25.jpg
--------------------------------------------------------------------------------
/images/26.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/26.jpg
--------------------------------------------------------------------------------
/images/27.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/27.jpg
--------------------------------------------------------------------------------
/images/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/3.jpg
--------------------------------------------------------------------------------
/images/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/4.jpg
--------------------------------------------------------------------------------
/images/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/5.jpg
--------------------------------------------------------------------------------
/images/6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/6.jpg
--------------------------------------------------------------------------------
/images/7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/7.jpg
--------------------------------------------------------------------------------
/images/8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/8.jpg
--------------------------------------------------------------------------------
/images/9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/9.jpg
--------------------------------------------------------------------------------
/images/chrome.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/chrome.jpg
--------------------------------------------------------------------------------
/images/firefox.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/firefox.jpg
--------------------------------------------------------------------------------
/images/ie.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/ie.jpg
--------------------------------------------------------------------------------
/images/opera.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/opera.jpg
--------------------------------------------------------------------------------
/images/transparent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/images/transparent.png
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fast-average-color-examples",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "fast-average-color-examples",
9 | "version": "1.0.0",
10 | "license": "MIT",
11 | "devDependencies": {
12 | "@rollup/plugin-node-resolve": "^16.0.1",
13 | "@rollup/plugin-typescript": "^12.1.2",
14 | "fast-average-color": "^9.5.0",
15 | "lyam": "^3.2.1",
16 | "rollup": "^4.36.0",
17 | "rollup-plugin-css-only": "^4.5.2",
18 | "tslib": "^2.8.1",
19 | "typescript": "^5.8.2"
20 | }
21 | },
22 | "node_modules/@rollup/plugin-node-resolve": {
23 | "version": "16.0.1",
24 | "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.1.tgz",
25 | "integrity": "sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==",
26 | "dev": true,
27 | "license": "MIT",
28 | "dependencies": {
29 | "@rollup/pluginutils": "^5.0.1",
30 | "@types/resolve": "1.20.2",
31 | "deepmerge": "^4.2.2",
32 | "is-module": "^1.0.0",
33 | "resolve": "^1.22.1"
34 | },
35 | "engines": {
36 | "node": ">=14.0.0"
37 | },
38 | "peerDependencies": {
39 | "rollup": "^2.78.0||^3.0.0||^4.0.0"
40 | },
41 | "peerDependenciesMeta": {
42 | "rollup": {
43 | "optional": true
44 | }
45 | }
46 | },
47 | "node_modules/@rollup/plugin-typescript": {
48 | "version": "12.1.2",
49 | "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-12.1.2.tgz",
50 | "integrity": "sha512-cdtSp154H5sv637uMr1a8OTWB0L1SWDSm1rDGiyfcGcvQ6cuTs4MDk2BVEBGysUWago4OJN4EQZqOTl/QY3Jgg==",
51 | "dev": true,
52 | "license": "MIT",
53 | "dependencies": {
54 | "@rollup/pluginutils": "^5.1.0",
55 | "resolve": "^1.22.1"
56 | },
57 | "engines": {
58 | "node": ">=14.0.0"
59 | },
60 | "peerDependencies": {
61 | "rollup": "^2.14.0||^3.0.0||^4.0.0",
62 | "tslib": "*",
63 | "typescript": ">=3.7.0"
64 | },
65 | "peerDependenciesMeta": {
66 | "rollup": {
67 | "optional": true
68 | },
69 | "tslib": {
70 | "optional": true
71 | }
72 | }
73 | },
74 | "node_modules/@rollup/pluginutils": {
75 | "version": "5.1.4",
76 | "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
77 | "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==",
78 | "dev": true,
79 | "license": "MIT",
80 | "dependencies": {
81 | "@types/estree": "^1.0.0",
82 | "estree-walker": "^2.0.2",
83 | "picomatch": "^4.0.2"
84 | },
85 | "engines": {
86 | "node": ">=14.0.0"
87 | },
88 | "peerDependencies": {
89 | "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
90 | },
91 | "peerDependenciesMeta": {
92 | "rollup": {
93 | "optional": true
94 | }
95 | }
96 | },
97 | "node_modules/@rollup/rollup-android-arm-eabi": {
98 | "version": "4.36.0",
99 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.36.0.tgz",
100 | "integrity": "sha512-jgrXjjcEwN6XpZXL0HUeOVGfjXhPyxAbbhD0BlXUB+abTOpbPiN5Wb3kOT7yb+uEtATNYF5x5gIfwutmuBA26w==",
101 | "cpu": [
102 | "arm"
103 | ],
104 | "dev": true,
105 | "license": "MIT",
106 | "optional": true,
107 | "os": [
108 | "android"
109 | ]
110 | },
111 | "node_modules/@rollup/rollup-android-arm64": {
112 | "version": "4.36.0",
113 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.36.0.tgz",
114 | "integrity": "sha512-NyfuLvdPdNUfUNeYKUwPwKsE5SXa2J6bCt2LdB/N+AxShnkpiczi3tcLJrm5mA+eqpy0HmaIY9F6XCa32N5yzg==",
115 | "cpu": [
116 | "arm64"
117 | ],
118 | "dev": true,
119 | "license": "MIT",
120 | "optional": true,
121 | "os": [
122 | "android"
123 | ]
124 | },
125 | "node_modules/@rollup/rollup-darwin-arm64": {
126 | "version": "4.36.0",
127 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.36.0.tgz",
128 | "integrity": "sha512-JQ1Jk5G4bGrD4pWJQzWsD8I1n1mgPXq33+/vP4sk8j/z/C2siRuxZtaUA7yMTf71TCZTZl/4e1bfzwUmFb3+rw==",
129 | "cpu": [
130 | "arm64"
131 | ],
132 | "dev": true,
133 | "license": "MIT",
134 | "optional": true,
135 | "os": [
136 | "darwin"
137 | ]
138 | },
139 | "node_modules/@rollup/rollup-darwin-x64": {
140 | "version": "4.36.0",
141 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.36.0.tgz",
142 | "integrity": "sha512-6c6wMZa1lrtiRsbDziCmjE53YbTkxMYhhnWnSW8R/yqsM7a6mSJ3uAVT0t8Y/DGt7gxUWYuFM4bwWk9XCJrFKA==",
143 | "cpu": [
144 | "x64"
145 | ],
146 | "dev": true,
147 | "license": "MIT",
148 | "optional": true,
149 | "os": [
150 | "darwin"
151 | ]
152 | },
153 | "node_modules/@rollup/rollup-freebsd-arm64": {
154 | "version": "4.36.0",
155 | "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.36.0.tgz",
156 | "integrity": "sha512-KXVsijKeJXOl8QzXTsA+sHVDsFOmMCdBRgFmBb+mfEb/7geR7+C8ypAml4fquUt14ZyVXaw2o1FWhqAfOvA4sg==",
157 | "cpu": [
158 | "arm64"
159 | ],
160 | "dev": true,
161 | "license": "MIT",
162 | "optional": true,
163 | "os": [
164 | "freebsd"
165 | ]
166 | },
167 | "node_modules/@rollup/rollup-freebsd-x64": {
168 | "version": "4.36.0",
169 | "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.36.0.tgz",
170 | "integrity": "sha512-dVeWq1ebbvByI+ndz4IJcD4a09RJgRYmLccwlQ8bPd4olz3Y213uf1iwvc7ZaxNn2ab7bjc08PrtBgMu6nb4pQ==",
171 | "cpu": [
172 | "x64"
173 | ],
174 | "dev": true,
175 | "license": "MIT",
176 | "optional": true,
177 | "os": [
178 | "freebsd"
179 | ]
180 | },
181 | "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
182 | "version": "4.36.0",
183 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.36.0.tgz",
184 | "integrity": "sha512-bvXVU42mOVcF4le6XSjscdXjqx8okv4n5vmwgzcmtvFdifQ5U4dXFYaCB87namDRKlUL9ybVtLQ9ztnawaSzvg==",
185 | "cpu": [
186 | "arm"
187 | ],
188 | "dev": true,
189 | "license": "MIT",
190 | "optional": true,
191 | "os": [
192 | "linux"
193 | ]
194 | },
195 | "node_modules/@rollup/rollup-linux-arm-musleabihf": {
196 | "version": "4.36.0",
197 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.36.0.tgz",
198 | "integrity": "sha512-JFIQrDJYrxOnyDQGYkqnNBtjDwTgbasdbUiQvcU8JmGDfValfH1lNpng+4FWlhaVIR4KPkeddYjsVVbmJYvDcg==",
199 | "cpu": [
200 | "arm"
201 | ],
202 | "dev": true,
203 | "license": "MIT",
204 | "optional": true,
205 | "os": [
206 | "linux"
207 | ]
208 | },
209 | "node_modules/@rollup/rollup-linux-arm64-gnu": {
210 | "version": "4.36.0",
211 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.36.0.tgz",
212 | "integrity": "sha512-KqjYVh3oM1bj//5X7k79PSCZ6CvaVzb7Qs7VMWS+SlWB5M8p3FqufLP9VNp4CazJ0CsPDLwVD9r3vX7Ci4J56A==",
213 | "cpu": [
214 | "arm64"
215 | ],
216 | "dev": true,
217 | "license": "MIT",
218 | "optional": true,
219 | "os": [
220 | "linux"
221 | ]
222 | },
223 | "node_modules/@rollup/rollup-linux-arm64-musl": {
224 | "version": "4.36.0",
225 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.36.0.tgz",
226 | "integrity": "sha512-QiGnhScND+mAAtfHqeT+cB1S9yFnNQ/EwCg5yE3MzoaZZnIV0RV9O5alJAoJKX/sBONVKeZdMfO8QSaWEygMhw==",
227 | "cpu": [
228 | "arm64"
229 | ],
230 | "dev": true,
231 | "license": "MIT",
232 | "optional": true,
233 | "os": [
234 | "linux"
235 | ]
236 | },
237 | "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
238 | "version": "4.36.0",
239 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.36.0.tgz",
240 | "integrity": "sha512-1ZPyEDWF8phd4FQtTzMh8FQwqzvIjLsl6/84gzUxnMNFBtExBtpL51H67mV9xipuxl1AEAerRBgBwFNpkw8+Lg==",
241 | "cpu": [
242 | "loong64"
243 | ],
244 | "dev": true,
245 | "license": "MIT",
246 | "optional": true,
247 | "os": [
248 | "linux"
249 | ]
250 | },
251 | "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
252 | "version": "4.36.0",
253 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.36.0.tgz",
254 | "integrity": "sha512-VMPMEIUpPFKpPI9GZMhJrtu8rxnp6mJR3ZzQPykq4xc2GmdHj3Q4cA+7avMyegXy4n1v+Qynr9fR88BmyO74tg==",
255 | "cpu": [
256 | "ppc64"
257 | ],
258 | "dev": true,
259 | "license": "MIT",
260 | "optional": true,
261 | "os": [
262 | "linux"
263 | ]
264 | },
265 | "node_modules/@rollup/rollup-linux-riscv64-gnu": {
266 | "version": "4.36.0",
267 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.36.0.tgz",
268 | "integrity": "sha512-ttE6ayb/kHwNRJGYLpuAvB7SMtOeQnVXEIpMtAvx3kepFQeowVED0n1K9nAdraHUPJ5hydEMxBpIR7o4nrm8uA==",
269 | "cpu": [
270 | "riscv64"
271 | ],
272 | "dev": true,
273 | "license": "MIT",
274 | "optional": true,
275 | "os": [
276 | "linux"
277 | ]
278 | },
279 | "node_modules/@rollup/rollup-linux-s390x-gnu": {
280 | "version": "4.36.0",
281 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.36.0.tgz",
282 | "integrity": "sha512-4a5gf2jpS0AIe7uBjxDeUMNcFmaRTbNv7NxI5xOCs4lhzsVyGR/0qBXduPnoWf6dGC365saTiwag8hP1imTgag==",
283 | "cpu": [
284 | "s390x"
285 | ],
286 | "dev": true,
287 | "license": "MIT",
288 | "optional": true,
289 | "os": [
290 | "linux"
291 | ]
292 | },
293 | "node_modules/@rollup/rollup-linux-x64-gnu": {
294 | "version": "4.36.0",
295 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.36.0.tgz",
296 | "integrity": "sha512-5KtoW8UWmwFKQ96aQL3LlRXX16IMwyzMq/jSSVIIyAANiE1doaQsx/KRyhAvpHlPjPiSU/AYX/8m+lQ9VToxFQ==",
297 | "cpu": [
298 | "x64"
299 | ],
300 | "dev": true,
301 | "license": "MIT",
302 | "optional": true,
303 | "os": [
304 | "linux"
305 | ]
306 | },
307 | "node_modules/@rollup/rollup-linux-x64-musl": {
308 | "version": "4.36.0",
309 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.36.0.tgz",
310 | "integrity": "sha512-sycrYZPrv2ag4OCvaN5js+f01eoZ2U+RmT5as8vhxiFz+kxwlHrsxOwKPSA8WyS+Wc6Epid9QeI/IkQ9NkgYyQ==",
311 | "cpu": [
312 | "x64"
313 | ],
314 | "dev": true,
315 | "license": "MIT",
316 | "optional": true,
317 | "os": [
318 | "linux"
319 | ]
320 | },
321 | "node_modules/@rollup/rollup-win32-arm64-msvc": {
322 | "version": "4.36.0",
323 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.36.0.tgz",
324 | "integrity": "sha512-qbqt4N7tokFwwSVlWDsjfoHgviS3n/vZ8LK0h1uLG9TYIRuUTJC88E1xb3LM2iqZ/WTqNQjYrtmtGmrmmawB6A==",
325 | "cpu": [
326 | "arm64"
327 | ],
328 | "dev": true,
329 | "license": "MIT",
330 | "optional": true,
331 | "os": [
332 | "win32"
333 | ]
334 | },
335 | "node_modules/@rollup/rollup-win32-ia32-msvc": {
336 | "version": "4.36.0",
337 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.36.0.tgz",
338 | "integrity": "sha512-t+RY0JuRamIocMuQcfwYSOkmdX9dtkr1PbhKW42AMvaDQa+jOdpUYysroTF/nuPpAaQMWp7ye+ndlmmthieJrQ==",
339 | "cpu": [
340 | "ia32"
341 | ],
342 | "dev": true,
343 | "license": "MIT",
344 | "optional": true,
345 | "os": [
346 | "win32"
347 | ]
348 | },
349 | "node_modules/@rollup/rollup-win32-x64-msvc": {
350 | "version": "4.36.0",
351 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.36.0.tgz",
352 | "integrity": "sha512-aRXd7tRZkWLqGbChgcMMDEHjOKudo1kChb1Jt1IfR8cY/KIpgNviLeJy5FUb9IpSuQj8dU2fAYNMPW/hLKOSTw==",
353 | "cpu": [
354 | "x64"
355 | ],
356 | "dev": true,
357 | "license": "MIT",
358 | "optional": true,
359 | "os": [
360 | "win32"
361 | ]
362 | },
363 | "node_modules/@types/estree": {
364 | "version": "1.0.6",
365 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
366 | "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
367 | "dev": true,
368 | "license": "MIT"
369 | },
370 | "node_modules/@types/resolve": {
371 | "version": "1.20.2",
372 | "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz",
373 | "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
374 | "dev": true,
375 | "license": "MIT"
376 | },
377 | "node_modules/deepmerge": {
378 | "version": "4.3.1",
379 | "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
380 | "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
381 | "dev": true,
382 | "license": "MIT",
383 | "engines": {
384 | "node": ">=0.10.0"
385 | }
386 | },
387 | "node_modules/estree-walker": {
388 | "version": "2.0.2",
389 | "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
390 | "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
391 | "dev": true,
392 | "license": "MIT"
393 | },
394 | "node_modules/fast-average-color": {
395 | "version": "9.5.0",
396 | "resolved": "https://registry.npmjs.org/fast-average-color/-/fast-average-color-9.5.0.tgz",
397 | "integrity": "sha512-nC6x2YIlJ9xxgkMFMd1BNoM1ctMjNoRKfRliPmiEWW3S6rLTHiQcy9g3pt/xiKv/D0NAAkhb9VyV+WJFvTqMGg==",
398 | "dev": true,
399 | "license": "MIT",
400 | "engines": {
401 | "node": ">= 12"
402 | }
403 | },
404 | "node_modules/fsevents": {
405 | "version": "2.3.3",
406 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
407 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
408 | "dev": true,
409 | "hasInstallScript": true,
410 | "license": "MIT",
411 | "optional": true,
412 | "os": [
413 | "darwin"
414 | ],
415 | "engines": {
416 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
417 | }
418 | },
419 | "node_modules/function-bind": {
420 | "version": "1.1.2",
421 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
422 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
423 | "dev": true,
424 | "license": "MIT",
425 | "funding": {
426 | "url": "https://github.com/sponsors/ljharb"
427 | }
428 | },
429 | "node_modules/hasown": {
430 | "version": "2.0.2",
431 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
432 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
433 | "dev": true,
434 | "license": "MIT",
435 | "dependencies": {
436 | "function-bind": "^1.1.2"
437 | },
438 | "engines": {
439 | "node": ">= 0.4"
440 | }
441 | },
442 | "node_modules/is-core-module": {
443 | "version": "2.16.1",
444 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
445 | "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
446 | "dev": true,
447 | "license": "MIT",
448 | "dependencies": {
449 | "hasown": "^2.0.2"
450 | },
451 | "engines": {
452 | "node": ">= 0.4"
453 | },
454 | "funding": {
455 | "url": "https://github.com/sponsors/ljharb"
456 | }
457 | },
458 | "node_modules/is-module": {
459 | "version": "1.0.0",
460 | "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
461 | "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
462 | "dev": true,
463 | "license": "MIT"
464 | },
465 | "node_modules/lyam": {
466 | "version": "3.2.1",
467 | "resolved": "https://registry.npmjs.org/lyam/-/lyam-3.2.1.tgz",
468 | "integrity": "sha512-yUsFFaCcHGE9tBAWQFUI5KdK8Cwv8l9yCPCZ5jxVONtRuBI78UKT9t9Wy0DAwXDfOuaOl5ymapPacY/mZZxJuA==",
469 | "dev": true,
470 | "license": "MIT",
471 | "engines": {
472 | "node": ">= 12"
473 | }
474 | },
475 | "node_modules/path-parse": {
476 | "version": "1.0.7",
477 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
478 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
479 | "dev": true,
480 | "license": "MIT"
481 | },
482 | "node_modules/picomatch": {
483 | "version": "4.0.2",
484 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
485 | "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
486 | "dev": true,
487 | "license": "MIT",
488 | "engines": {
489 | "node": ">=12"
490 | },
491 | "funding": {
492 | "url": "https://github.com/sponsors/jonschlinkert"
493 | }
494 | },
495 | "node_modules/resolve": {
496 | "version": "1.22.10",
497 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
498 | "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
499 | "dev": true,
500 | "license": "MIT",
501 | "dependencies": {
502 | "is-core-module": "^2.16.0",
503 | "path-parse": "^1.0.7",
504 | "supports-preserve-symlinks-flag": "^1.0.0"
505 | },
506 | "bin": {
507 | "resolve": "bin/resolve"
508 | },
509 | "engines": {
510 | "node": ">= 0.4"
511 | },
512 | "funding": {
513 | "url": "https://github.com/sponsors/ljharb"
514 | }
515 | },
516 | "node_modules/rollup": {
517 | "version": "4.36.0",
518 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.36.0.tgz",
519 | "integrity": "sha512-zwATAXNQxUcd40zgtQG0ZafcRK4g004WtEl7kbuhTWPvf07PsfohXl39jVUvPF7jvNAIkKPQ2XrsDlWuxBd++Q==",
520 | "dev": true,
521 | "license": "MIT",
522 | "dependencies": {
523 | "@types/estree": "1.0.6"
524 | },
525 | "bin": {
526 | "rollup": "dist/bin/rollup"
527 | },
528 | "engines": {
529 | "node": ">=18.0.0",
530 | "npm": ">=8.0.0"
531 | },
532 | "optionalDependencies": {
533 | "@rollup/rollup-android-arm-eabi": "4.36.0",
534 | "@rollup/rollup-android-arm64": "4.36.0",
535 | "@rollup/rollup-darwin-arm64": "4.36.0",
536 | "@rollup/rollup-darwin-x64": "4.36.0",
537 | "@rollup/rollup-freebsd-arm64": "4.36.0",
538 | "@rollup/rollup-freebsd-x64": "4.36.0",
539 | "@rollup/rollup-linux-arm-gnueabihf": "4.36.0",
540 | "@rollup/rollup-linux-arm-musleabihf": "4.36.0",
541 | "@rollup/rollup-linux-arm64-gnu": "4.36.0",
542 | "@rollup/rollup-linux-arm64-musl": "4.36.0",
543 | "@rollup/rollup-linux-loongarch64-gnu": "4.36.0",
544 | "@rollup/rollup-linux-powerpc64le-gnu": "4.36.0",
545 | "@rollup/rollup-linux-riscv64-gnu": "4.36.0",
546 | "@rollup/rollup-linux-s390x-gnu": "4.36.0",
547 | "@rollup/rollup-linux-x64-gnu": "4.36.0",
548 | "@rollup/rollup-linux-x64-musl": "4.36.0",
549 | "@rollup/rollup-win32-arm64-msvc": "4.36.0",
550 | "@rollup/rollup-win32-ia32-msvc": "4.36.0",
551 | "@rollup/rollup-win32-x64-msvc": "4.36.0",
552 | "fsevents": "~2.3.2"
553 | }
554 | },
555 | "node_modules/rollup-plugin-css-only": {
556 | "version": "4.5.2",
557 | "resolved": "https://registry.npmjs.org/rollup-plugin-css-only/-/rollup-plugin-css-only-4.5.2.tgz",
558 | "integrity": "sha512-7rj9+jB17Pz8LNcPgtMUb16JcgD8lxQMK9HcGfAVhMK3na/WXes3oGIo5QsrQQVqtgAU6q6KnQNXJrYunaUIQQ==",
559 | "dev": true,
560 | "license": "MIT",
561 | "dependencies": {
562 | "@rollup/pluginutils": "5"
563 | },
564 | "engines": {
565 | "node": ">=14"
566 | },
567 | "peerDependencies": {
568 | "rollup": "<5"
569 | }
570 | },
571 | "node_modules/supports-preserve-symlinks-flag": {
572 | "version": "1.0.0",
573 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
574 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
575 | "dev": true,
576 | "license": "MIT",
577 | "engines": {
578 | "node": ">= 0.4"
579 | },
580 | "funding": {
581 | "url": "https://github.com/sponsors/ljharb"
582 | }
583 | },
584 | "node_modules/tslib": {
585 | "version": "2.8.1",
586 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
587 | "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
588 | "dev": true,
589 | "license": "0BSD"
590 | },
591 | "node_modules/typescript": {
592 | "version": "5.8.2",
593 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
594 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
595 | "dev": true,
596 | "license": "Apache-2.0",
597 | "bin": {
598 | "tsc": "bin/tsc",
599 | "tsserver": "bin/tsserver"
600 | },
601 | "engines": {
602 | "node": ">=14.17"
603 | }
604 | }
605 | }
606 | }
607 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fast-average-color-examples",
3 | "version": "1.0.0",
4 | "description": "Examples for Fast Average Color",
5 | "scripts": {
6 | "build": "rollup -c",
7 | "typecheck": "tsc --noEmit",
8 | "test": "npm run typecheck"
9 | },
10 | "repository": {
11 | "type": "git",
12 | "url": "git+https://github.com/fast-average-color/examples.git"
13 | },
14 | "keywords": [
15 | "examples",
16 | "average color"
17 | ],
18 | "author": "Denis Seleznev",
19 | "license": "MIT",
20 | "bugs": {
21 | "url": "https://github.com/fast-average-color/examples/issues"
22 | },
23 | "homepage": "https://github.com/fast-average-color/examples#readme",
24 | "private": true,
25 | "devDependencies": {
26 | "@rollup/plugin-node-resolve": "^16.0.1",
27 | "@rollup/plugin-typescript": "^12.1.2",
28 | "fast-average-color": "^9.5.0",
29 | "lyam": "^3.2.1",
30 | "rollup": "^4.36.0",
31 | "rollup-plugin-css-only": "^4.5.2",
32 | "tslib": "^2.8.1",
33 | "typescript": "^5.8.2"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/rollup.config.mjs:
--------------------------------------------------------------------------------
1 | import typescript from '@rollup/plugin-typescript';
2 | import { nodeResolve } from '@rollup/plugin-node-resolve';
3 | import css from 'rollup-plugin-css-only';
4 |
5 | const createConfig = name => ({
6 | input: `src/${name}/index.ts`,
7 | output: [
8 | {
9 | file: `dist/${name}.js`,
10 | format: 'umd',
11 | },
12 | ] ,
13 | plugins: [
14 | typescript(),
15 | nodeResolve(),
16 | css({
17 | output: `${name}.css`,
18 | }),
19 | ]
20 | });
21 |
22 | const configs = [
23 | 'algorithms',
24 | 'ambilight',
25 | 'background',
26 | 'background_async',
27 | 'background_async_dominant',
28 | 'box-shadow',
29 | 'box-shadow-4-sides',
30 | 'canvas',
31 | 'border',
32 | 'text-photo',
33 | 'gradient',
34 | 'gradient_stripes',
35 | 'gallery',
36 | 'gallery_vertical',
37 | 'define',
38 | 'timeline',
39 | ].map(name => createConfig(name));
40 |
41 | export default configs;
42 |
--------------------------------------------------------------------------------
/src/algorithms/build.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const template = fs.readFileSync('./template.html', 'utf8');
5 | const MAX_PHOTO = 27;
6 |
7 | let rows = '';
8 | for (let i = 1; i <= MAX_PHOTO; i++) {
9 | rows += `
10 |

11 |
12 |
13 |
14 |
`
15 | }
16 |
17 | fs.writeFileSync('../../algorithms.html', template.replace(/\{ROWS\}/, rows));
18 |
--------------------------------------------------------------------------------
/src/algorithms/index.css:
--------------------------------------------------------------------------------
1 | .rows {
2 | margin: 50px 0 0 20px;
3 | color: #fff;
4 | }
5 |
6 | .row
7 | {
8 | display: flex;
9 | }
10 |
11 | .item
12 | {
13 | transition: all .3s ease;
14 | flex-basis: 300px;
15 | margin: 20px 20px 0 0;
16 | text-align: center;
17 | }
18 |
19 | .item_image {
20 | max-width: 300px;
21 | width: 25%;
22 | }
23 |
--------------------------------------------------------------------------------
/src/algorithms/index.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor } from 'fast-average-color';
2 | import '../common';
3 | import './index.css';
4 |
5 | const fac = new FastAverageColor();
6 |
7 | window.addEventListener('load', () => {
8 | Array.from(document.querySelectorAll('.row')).forEach(row => {
9 | (['simple', 'sqrt', 'dominant'] as const).forEach(algorithm => {
10 | fac.getColorAsync(row.querySelector('.item_image'), { algorithm })
11 | .then(color => {
12 | const item = row.querySelector('.item_' + algorithm) as HTMLDivElement;
13 |
14 | item.style.backgroundColor = color.rgb;
15 | item.style.color = color.isDark ? '#fff' : '#000';
16 | item.innerText = color.hex;
17 | })
18 | .catch(e => console.log(e));
19 | });
20 | });
21 | }, false);
22 |
--------------------------------------------------------------------------------
/src/algorithms/template.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Average color: Algorithms
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
Photo
15 |
simple algorithm
16 |
sqrt algorithm
17 |
dominant algorithm
18 |
19 | {ROWS}
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/src/ambilight/ambilight.ts:
--------------------------------------------------------------------------------
1 | import '../common';
2 | import './index.css';
3 |
4 | import { Ambilight4Sides } from './types/4sides';
5 | import { AmbilightManyPoints } from './types/many-points';
6 | import { AmbilightBase } from './types/base';
7 |
8 | interface AmbilightOptions {
9 | onPlay?: () => void;
10 | }
11 |
12 | interface AmbilightTypeItem {
13 | name: string;
14 | klass: typeof AmbilightManyPoints | typeof Ambilight4Sides,
15 | options: any;
16 | }
17 |
18 | export class Ambilight {
19 | requestId = 0;
20 |
21 | plugin: AmbilightBase | null;
22 |
23 | video: HTMLVideoElement | null;
24 | container: HTMLElement | null;
25 | options: AmbilightOptions;
26 |
27 | types: AmbilightTypeItem[] = [
28 | { name: 'ManyPoints', klass: AmbilightManyPoints, options: { countByWidth: 10, countByHeight: 5, size: 70 }},
29 | { name: '4Sides', klass: Ambilight4Sides, options: { radius: 200, delta: 200, size: 70 }},
30 | ];
31 |
32 | constructor(video: HTMLVideoElement, container: HTMLElement, options?: AmbilightOptions) {
33 | this.onPlay = this.onPlay.bind(this);
34 | this.onPause = this.onPause.bind(this);
35 | this.onUpdate = this.onUpdate.bind(this)
36 |
37 | this.video = video;
38 | this.video.addEventListener('play', this.onPlay.bind(this), false);
39 | this.video.addEventListener('pause', this.onPause, false);
40 |
41 | this.container = container;
42 | this.options = options || {};
43 |
44 | this.plugin = new this.types[0].klass(video, container, this.types[0].options);
45 | !video.paused && this.onPlay();
46 | }
47 |
48 | setType(name: string) {
49 | const plugin = this.types.find(item => item.name === name);
50 | if (plugin && this.video && this.container) {
51 | this.plugin?.destroy();
52 | this.plugin = new plugin.klass(this.video, this.container, plugin.options);
53 | }
54 | }
55 |
56 | private onPlay() {
57 | this.options.onPlay?.();
58 |
59 | this.requestId = window.requestAnimationFrame(this.onUpdate);
60 | }
61 |
62 | private onPause() {
63 | window.cancelAnimationFrame(this.requestId);
64 | }
65 |
66 | private onUpdate() {
67 | this.plugin?.onUpdate();
68 | this.requestId = window.requestAnimationFrame(this.onUpdate);
69 | }
70 |
71 | destroy() {
72 | if (this.plugin) {
73 | this.plugin.destroy();
74 | this.plugin = null;
75 | }
76 |
77 | if (this.video) {
78 | this.video.removeEventListener('play', this.onPlay, false);
79 | this.video.removeEventListener('pause', this.onPause, false);
80 | }
81 |
82 | window.cancelAnimationFrame(this.requestId);
83 | }
84 | };
85 |
--------------------------------------------------------------------------------
/src/ambilight/index.css:
--------------------------------------------------------------------------------
1 | .ambilight-control
2 | {
3 | font-size: 12px;
4 | line-height: 12px;
5 |
6 | position: fixed;
7 | z-index: 100;
8 | top: 21px;
9 | left: 50%;
10 |
11 | transform: translateX(-50%);
12 | text-align: center;
13 | white-space: nowrap;
14 |
15 | color: #999;
16 | }
17 |
18 | .ambilight-control h1
19 | {
20 | font-weight: normal;
21 |
22 | margin: 0 0 15px 0;
23 | padding: 0;
24 |
25 | color: #aaa;
26 | }
27 |
28 | .ambilight-control label
29 | {
30 | margin: 0;
31 | margin-right: 8px;
32 | padding: 0;
33 | }
34 |
35 | .ambilight-control input
36 | {
37 | font-size: 12px;
38 | line-height: 12px;
39 |
40 | margin: 0 2px 0 0;
41 |
42 | vertical-align: top;
43 | }
44 |
45 | .video-container
46 | {
47 | position: absolute;
48 | top: 50%;
49 | left: 50%;
50 |
51 | transform: translate(-50%, -50%);
52 | }
53 |
54 | video
55 | {
56 | position: relative;
57 | z-index: 100;
58 |
59 | display: block;
60 |
61 | margin: 0;
62 | padding: 0;
63 |
64 | border: 10px solid black;
65 | outline: 1px solid rgba(255, 255, 255, .1);
66 | background: black;
67 | }
68 |
69 | .video-shadow
70 | {
71 | position: absolute;
72 | }
73 |
74 | .video-shadow_left
75 | {
76 | left: 0;
77 |
78 | border-radius: 100% 0 0 100%;
79 | }
80 |
81 | .video-shadow_right
82 | {
83 | right: 0;
84 |
85 | border-radius: 0 100% 100% 0;
86 | }
87 |
88 | .video-shadow_top
89 | {
90 | top: 0;
91 |
92 | border-radius: 100% 100% 0 0;
93 | }
94 |
95 | .video-shadow_bottom
96 | {
97 | bottom: 0;
98 |
99 | border-radius: 0 0 100% 100%;
100 | }
101 |
102 | .copyright
103 | {
104 | font-family: Arial;
105 | font-size: 12px;
106 |
107 | position: fixed;
108 | bottom: 5px;
109 | left: 50%;
110 |
111 | margin-left: -5em;
112 | }
113 |
114 | .hint
115 | {
116 | font-size: 16px;
117 |
118 | position: absolute;
119 | top: 50%;
120 | left: 50%;
121 |
122 | margin-top: 200px;
123 | margin-left: -320px;
124 |
125 | color: #aaa;
126 | }
127 |
128 | .copyright:link,
129 | .copyright:visited,
130 | .copyright:active,
131 | .copyright:hover
132 | {
133 | color: #555;
134 | }
135 |
--------------------------------------------------------------------------------
/src/ambilight/index.ts:
--------------------------------------------------------------------------------
1 | import { Ambilight } from './ambilight';
2 | import '../common';
3 | import './index.css';
4 |
5 | window.addEventListener('load', function() {
6 | const video = document.querySelector('video') as HTMLVideoElement;
7 | const container = document.querySelector('.video-container') as HTMLElement;
8 | const ambi = new Ambilight(video, container, {
9 | onPlay() {
10 | const hint = document.querySelector('.hint') as HTMLElement;
11 | hint.style.display = 'none';
12 | },
13 | });
14 |
15 | video.muted = true;
16 | video.play();
17 |
18 | const inputs = document.querySelectorAll('input[type="radio"]');
19 | inputs[location.hash === '#4Sides' ? 1 : 0].checked = true;
20 |
21 | inputs[0].onchange = inputs[1].onchange = (event: Event) => {
22 | const target = event.target as HTMLInputElement;
23 | ambi.setType(target.value);
24 | location.hash = target.value;
25 | };
26 | }, false);
27 |
--------------------------------------------------------------------------------
/src/ambilight/types/4sides.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor } from 'fast-average-color';
2 | import { AmbilightBase } from './base';
3 |
4 | const fac = new FastAverageColor();
5 |
6 | interface Ambilight4SideOptions {
7 | radius: number;
8 | delta: number;
9 | size: number;
10 | }
11 |
12 | export class Ambilight4Sides extends AmbilightBase {
13 | video: HTMLVideoElement | null;
14 | container: HTMLElement | null;
15 |
16 | options: Ambilight4SideOptions;
17 |
18 | constructor(video: HTMLVideoElement, container: HTMLElement, options: Ambilight4SideOptions) {
19 | super(video, container, options);
20 |
21 | this.video = video;
22 | this.container = container;
23 | this.options = options;
24 | }
25 |
26 | destroy() {
27 | if (this.container) {
28 | this.container.style.boxShadow = 'none';
29 | }
30 |
31 | this.video = null;
32 | this.container = null;
33 | }
34 |
35 | onUpdate() {
36 | if (!this.video || !this.container) {
37 | return;
38 | }
39 |
40 | const width = this.video.videoWidth;
41 | const height = this.video.videoHeight;
42 | const { size } = this.options;
43 | const video = this.video;
44 |
45 |
46 | const colorTop = fac.getColor(video, {
47 | left: 0,
48 | top: 0,
49 | height: size,
50 | width: width
51 | });
52 |
53 | const colorRight = fac.getColor(video, {
54 | left: width - size,
55 | top: 0,
56 | width: size,
57 | height: height
58 | });
59 | const colorLeft = fac.getColor(video, {
60 | left: 0,
61 | top: 0,
62 | width: size,
63 | height: height
64 | });
65 | const colorBottom = fac.getColor(video, {
66 | left: 0,
67 | top: height - size,
68 | width: width,
69 | height: size
70 | });
71 |
72 | const radius = this.options.radius + 'px';
73 | const delta = this.options.delta + 'px';
74 |
75 | this.container.style.boxShadow = [
76 | `0 -${delta} ${radius} ${colorTop.rgb}`,
77 | `${delta} 0 ${radius} ${colorRight.rgb}`,
78 | `0 ${delta} ${radius} ${colorBottom.rgb}`,
79 | `-${delta} 0 ${radius} ${colorLeft.rgb}`
80 | ].join(', ');
81 | }
82 | };
83 |
--------------------------------------------------------------------------------
/src/ambilight/types/base.ts:
--------------------------------------------------------------------------------
1 | export abstract class AmbilightBase {
2 | constructor(_video: HTMLVideoElement, _container: HTMLElement, _options: any) {};
3 |
4 | abstract onUpdate(): void;
5 | abstract destroy(): void;
6 | }
7 |
--------------------------------------------------------------------------------
/src/ambilight/types/helpers/ua.ts:
--------------------------------------------------------------------------------
1 | const ua = navigator.userAgent.toLowerCase();
2 | export const isFirefox = ua.indexOf('firefox') > -1;
3 | export const isSafari = (ua.search('safari') > -1 && ua.search('chrome') === -1);
4 |
--------------------------------------------------------------------------------
/src/ambilight/types/many-points.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor } from 'fast-average-color';
2 | import { AmbilightBase } from './base';
3 |
4 | import { isSafari, isFirefox } from './helpers/ua';
5 |
6 | const fac = new FastAverageColor();
7 |
8 | interface AmbilightManyPointsOptions {
9 | countByWidth: number;
10 | countByHeight: number;
11 | size: number;
12 | }
13 |
14 | export class AmbilightManyPoints extends AmbilightBase {
15 | hasDoubleBlur = false;
16 |
17 | topElems: HTMLDivElement[] = [];
18 | bottomElems: HTMLDivElement[] = [];
19 | leftElems: HTMLDivElement[] = [];
20 | rightElems: HTMLDivElement[] = [];
21 |
22 | video: HTMLVideoElement | null;
23 | container: HTMLElement | null;
24 |
25 | options: AmbilightManyPointsOptions;
26 |
27 | constructor(video: HTMLVideoElement, container: HTMLElement, options: AmbilightManyPointsOptions) {
28 | super(video, container, options);
29 |
30 | this.video = video;
31 | this.container = container;
32 | this.options = options;
33 |
34 | this.hasDoubleBlur = isFirefox || isSafari ? false : true;
35 |
36 | this.createShadows();
37 |
38 | this.updateShadows = this.updateShadows.bind(this);
39 |
40 | console.log('init plugin');
41 | }
42 |
43 | destroy() {
44 | if (this.container) {
45 | for (let i = 0; i < this.topElems.length; i++) {
46 | this.container.removeChild(this.topElems[i]);
47 | this.container.removeChild(this.bottomElems[i]);
48 | }
49 |
50 | for (let i = 0; i < this.leftElems.length; i++) {
51 | this.container.removeChild(this.leftElems[i]);
52 | this.container.removeChild(this.rightElems[i]);
53 | }
54 | }
55 |
56 | this.video = null;
57 | this.container = null;
58 | }
59 |
60 | createShadows() {
61 | const { countByWidth, countByHeight } = this.options;
62 |
63 | const width = this.video?.videoWidth || 0;
64 | const height = this.video?.videoHeight || 0;
65 |
66 | this.topElems = [];
67 | this.bottomElems = [];
68 | this.leftElems = [];
69 | this.rightElems = [];
70 |
71 | for (let i = 0; i < countByWidth; i++) {
72 | const size = width / countByWidth;
73 | const topElem = this.createShadow('top');
74 | const left = (i * size) + 'px';
75 | topElem.style.left = left;
76 | this.topElems.push(topElem);
77 | this.container?.appendChild(topElem);
78 |
79 | const bottomElem = this.createShadow('bottom');
80 | bottomElem.style.left = left;
81 | this.bottomElems.push(bottomElem);
82 | this.container?.appendChild(bottomElem);
83 | }
84 |
85 | for (let i = 0; i < countByHeight; i++) {
86 | const leftElem = this.createShadow('left');
87 | const size = height / countByHeight;
88 |
89 | const top = (i * size) + 'px';
90 | leftElem.style.top = top;
91 | this.leftElems.push(leftElem);
92 | this.container?.appendChild(leftElem);
93 |
94 | const rightElem = this.createShadow('right');
95 | rightElem.style.top = top;
96 | this.rightElems.push(rightElem);
97 | this.container?.appendChild(rightElem);
98 | }
99 | }
100 |
101 | createShadow(position: string) {
102 | const { size } = this.options;
103 | const elem = document.createElement('div');
104 |
105 | elem.className = 'video-shadow video-shadow_' + position;
106 | elem.style.width = `${size}px`;
107 | elem.style.height = `${size}px`;
108 |
109 | return elem;
110 | }
111 |
112 | updateShadows() {
113 | console.log('updateShadows plugin');
114 |
115 | const width = this.video?.videoWidth || 0;
116 | const height = this.video?.videoHeight || 0;
117 |
118 | const { video } = this;
119 | const { countByHeight, countByWidth } = this.options;
120 |
121 | for (let i = 0; i < this.leftElems.length; i++) {
122 | const size = Math.floor(height / countByHeight);
123 |
124 | const leftColor = fac.getColor(video, {
125 | left: 0,
126 | top: size * i,
127 | width: size,
128 | height: size
129 | });
130 |
131 | const rightColor = fac.getColor(video, {
132 | left: width - size,
133 | top: size * i,
134 | width: size,
135 | height: size
136 | });
137 |
138 | const offset = size;
139 | const blur = this.hasDoubleBlur ? size * 2 : size;
140 |
141 | this.leftElems[i].style.boxShadow = '-' + offset + 'px 0 ' + blur + 'px ' + leftColor.rgb;
142 | this.rightElems[i].style.boxShadow = offset + 'px 0 ' + blur + 'px ' + rightColor.rgb;
143 | }
144 |
145 | for (let i = 0; i < this.topElems.length; i++) {
146 | const size = Math.floor(width / countByWidth);
147 |
148 | const topColor = fac.getColor(video, {
149 | left: size * i,
150 | top: 0,
151 | width: size,
152 | height: size
153 | });
154 |
155 | const bottomColor = fac.getColor(video, {
156 | left: size * i,
157 | top: height - size,
158 | width: size,
159 | height: size
160 | });
161 |
162 | const offset = size;
163 | const blur = this.hasDoubleBlur ? size * 2 : size;
164 |
165 | this.topElems[i].style.boxShadow = '0 -' + offset + 'px ' + blur + 'px ' + topColor.rgb;
166 | this.bottomElems[i].style.boxShadow = '0 ' + offset + 'px ' + blur + 'px ' + bottomColor.rgb;
167 | }
168 | }
169 |
170 | onUpdate() {
171 | this.updateShadows();
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/src/background/index.css:
--------------------------------------------------------------------------------
1 | img
2 | {
3 | width: 100%;
4 | }
5 |
6 | .item
7 | {
8 | float: left;
9 |
10 | box-sizing: border-box;
11 | width: 500px;
12 | max-width: 100%;
13 | padding: 50px 50px 45px 50px;
14 |
15 | transition: background-color .5s ease;
16 | }
17 |
18 | .item__text
19 | {
20 | padding-top: 10px;
21 | }
22 |
--------------------------------------------------------------------------------
/src/background/index.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor } from 'fast-average-color';
2 |
3 | import '../common';
4 | import './index.css';
5 |
6 | const fac = new FastAverageColor();
7 |
8 | window.addEventListener('load', function() {
9 | Array.from(document.querySelectorAll('.item')).forEach(item => {
10 | const color = fac.getColor(item.querySelector('img'));
11 |
12 | item.style.backgroundColor = color.rgb;
13 | item.style.color = color.isDark ? 'white' : 'black';
14 | });
15 | }, false);
16 |
--------------------------------------------------------------------------------
/src/background_async/index.css:
--------------------------------------------------------------------------------
1 | img
2 | {
3 | width: 100%;
4 | }
5 |
6 | .item
7 | {
8 | float: left;
9 |
10 | box-sizing: border-box;
11 | width: 500px;
12 | max-width: 100%;
13 | padding: 50px 50px 45px 50px;
14 |
15 | transition: background-color .5s ease;
16 | }
17 |
18 | .item__text
19 | {
20 | padding-top: 10px;
21 | }
22 |
--------------------------------------------------------------------------------
/src/background_async/index.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor } from 'fast-average-color';
2 |
3 | import '../common';
4 | import './index.css';
5 |
6 | const fac = new FastAverageColor();
7 |
8 | Array.from(document.querySelectorAll('.item')).forEach(item => {
9 | fac.getColorAsync(item.querySelector('img'))
10 | .then(color => {
11 | item.style.backgroundColor = color.rgb;
12 | item.style.color = color.isDark ? 'white' : 'black';
13 | })
14 | .catch(e => {
15 | console.log(e);
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/background_async_dominant/index.css:
--------------------------------------------------------------------------------
1 | img
2 | {
3 | width: 100%;
4 | }
5 |
6 | .item
7 | {
8 | float: left;
9 |
10 | box-sizing: border-box;
11 | width: 500px;
12 | max-width: 100%;
13 | padding: 50px 50px 45px 50px;
14 |
15 | transition: background-color .5s ease;
16 | }
17 |
18 | .item__text
19 | {
20 | padding-top: 10px;
21 | }
22 |
--------------------------------------------------------------------------------
/src/background_async_dominant/index.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor } from 'fast-average-color';
2 |
3 | import '../common';
4 | import './index.css';
5 |
6 | const fac = new FastAverageColor();
7 |
8 | Array.from(document.querySelectorAll('.item')).forEach(item => {
9 | fac.getColorAsync(item.querySelector('img'), { algorithm: 'dominant' })
10 | .then(color => {
11 | item.style.backgroundColor = color.rgb;
12 | item.style.color = color.isDark ? 'white' : 'black';
13 | })
14 | .catch(e => {
15 | console.log(e);
16 | });
17 | });
18 |
--------------------------------------------------------------------------------
/src/border/index.css:
--------------------------------------------------------------------------------
1 | .item
2 | {
3 | float: left;
4 |
5 | box-sizing: border-box;
6 | width: 400px;
7 | max-width: 100%;
8 | padding: 30px;
9 |
10 | transition: border-color .5s ease;
11 | }
12 |
13 | .item__image-container
14 | {
15 | transition: all .3s ease;
16 |
17 | border: 40px solid #000;
18 | outline: 1px solid rgba(255, 255, 255, .08);
19 | }
20 |
21 | .item__image
22 | {
23 | display: block;
24 |
25 | width: 100%;
26 |
27 | outline: 1px solid rgba(255, 255, 255, .08);
28 | }
29 |
30 | .item__text
31 | {
32 | position: relative;
33 | z-index: 10;
34 |
35 | padding: 10px;
36 |
37 | color: #fff;
38 | }
39 |
--------------------------------------------------------------------------------
/src/border/index.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor } from 'fast-average-color';
2 |
3 | import '../common';
4 | import './index.css';
5 |
6 | const fac = new FastAverageColor();
7 |
8 | window.addEventListener('load', function() {
9 | Array.from(document.querySelectorAll('.item')).forEach(item => {
10 | const imageContainer = item.querySelector('.item__image-container') as HTMLDivElement;
11 | const image = item.querySelector('.item__image') as HTMLImageElement;
12 |
13 | const size = 20;
14 |
15 | const width = image.naturalWidth;
16 | const height = image.naturalHeight;
17 |
18 | const colorTop = fac.getColor(image, {height: size});
19 | const colorRight = fac.getColor(image, {left: width - size, width: size});
20 | const colorLeft = fac.getColor(image, {width: size});
21 | const colorBottom = fac.getColor(image, {top: height - size, height: size});
22 |
23 | imageContainer.style.borderColor = [
24 | colorTop.rgb,
25 | colorRight.rgb,
26 | colorBottom.rgb,
27 | colorLeft.rgb
28 | ].join(' ');
29 | });
30 | }, false);
31 |
--------------------------------------------------------------------------------
/src/box-shadow-4-sides/index.css:
--------------------------------------------------------------------------------
1 | img
2 | {
3 | width: 100%;
4 |
5 | transition: all .3s ease;
6 |
7 | border-radius: 50px;
8 | }
9 |
10 | .item
11 | {
12 | float: left;
13 |
14 | box-sizing: border-box;
15 | width: 540px;
16 | max-width: 100%;
17 | padding: 70px;
18 |
19 | transition: background-color .5s ease;
20 | }
21 |
22 | .item__text
23 | {
24 | position: relative;
25 | z-index: 10;
26 |
27 | padding: 10px;
28 | }
29 |
--------------------------------------------------------------------------------
/src/box-shadow-4-sides/index.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor } from 'fast-average-color';
2 |
3 | import '../common';
4 | import './index.css';
5 |
6 | const fac = new FastAverageColor();
7 |
8 | window.addEventListener('load', () => {
9 | Array.from(document.querySelectorAll('.item')).forEach(item => {
10 | const image = item.querySelector('img') as HTMLImageElement;
11 | const size = 50;
12 |
13 | const width = image.naturalWidth;
14 | const height = image.naturalHeight;
15 |
16 | const colorTop = fac.getColor(image, {height: size});
17 | const colorRight = fac.getColor(image, {left: width - size, width: size});
18 | const colorLeft = fac.getColor(image, {width: size});
19 | const colorBottom = fac.getColor(image, {top: height - size, height: size});
20 | const radius = ' 90px ';
21 | const delta = '70px';
22 |
23 | image.style.boxShadow = [
24 | `0 -${delta} ${radius} ${colorTop.rgb}`,
25 | `${delta} 0 ${radius} ${colorRight.rgb}`,
26 | `0 ${delta} ${radius} ${colorBottom.rgb}`,
27 | `-${delta} 0 ${radius} ${colorLeft.rgb}`,
28 | ].join(', ');
29 |
30 | item.style.color = colorBottom.isDark ? 'white' : 'black';
31 | });
32 | }, false);
33 |
--------------------------------------------------------------------------------
/src/box-shadow/index.css:
--------------------------------------------------------------------------------
1 | img
2 | {
3 | width: 100%;
4 |
5 | transition: all .3s ease;
6 |
7 | border-radius: 50px;
8 | }
9 |
10 | img:hover
11 | {
12 | transform: translateY(10px);
13 |
14 | opacity: .8;
15 | }
16 |
17 | .item
18 | {
19 | float: left;
20 |
21 | box-sizing: border-box;
22 | width: 540px;
23 | max-width: 100%;
24 | padding: 30px 50px;
25 |
26 | transition: background-color .5s ease;
27 | }
28 |
29 | .item__text
30 | {
31 | position: relative;
32 | z-index: 10;
33 |
34 | padding: 10px;
35 | }
36 |
--------------------------------------------------------------------------------
/src/box-shadow/index.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor } from 'fast-average-color';
2 |
3 | import '../common';
4 | import './index.css';
5 |
6 | const fac = new FastAverageColor();
7 |
8 | window.addEventListener('load', function() {
9 | Array.from(document.querySelectorAll('.item')).forEach(item => {
10 | const image = item.querySelector('img');
11 | const color = fac.getColor(image);
12 |
13 | if (image) {
14 | image.style.boxShadow = '0 70px 90px ' + color.rgb;
15 | item.style.color = color.isDark ? 'white' : 'black';
16 | }
17 | });
18 | }, false);
19 |
--------------------------------------------------------------------------------
/src/canvas/helpers.ts:
--------------------------------------------------------------------------------
1 | export const rnd = (max: number) => {
2 | return Math.random() * max;
3 | };
4 |
5 | export const rndFloor = (max: number) => {
6 | return Math.floor(Math.random() * max);
7 | };
8 |
--------------------------------------------------------------------------------
/src/canvas/index.css:
--------------------------------------------------------------------------------
1 | canvas
2 | {
3 | position: fixed;
4 | top: 50%;
5 | left: 50%;
6 |
7 | transform: translate(-50%, -50%);
8 |
9 | border: 1px solid rgba(100, 100, 100, .5);
10 | }
11 |
12 | .info
13 | {
14 | font-family: Arial;
15 | font-size: 16px;
16 |
17 | position: fixed;
18 | z-index: 10;
19 | top: 50px;
20 | left: 50px;
21 |
22 | padding: 20px;
23 |
24 | color: white;
25 | border-radius: 10px;
26 | background: rgba(255, 255, 255, .1);
27 | text-shadow: 1px 1px 2px black;
28 | }
29 |
30 | .info__item
31 | {
32 | overflow-x: hidden;
33 |
34 | width: 250px;
35 |
36 | white-space: nowrap;
37 | text-overflow: ellipsis;
38 | }
39 |
40 | .info__item, .option {
41 | padding: 3px;
42 | }
43 |
44 | .option {
45 | margin: 5px 0 5px 0;
46 | }
47 |
48 | #start {
49 | font-size: 14px;
50 | padding: 10px;
51 | width: 100%;
52 | box-sizing: border-box;
53 | }
54 |
--------------------------------------------------------------------------------
/src/canvas/index.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor } from 'fast-average-color';
2 | import { rnd, rndFloor } from './helpers';
3 |
4 | import '../common';
5 | import './index.css';
6 |
7 | const fac = new FastAverageColor();
8 |
9 | class App {
10 | canvas = document.querySelector('canvas') as HTMLCanvasElement;
11 | ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D;
12 |
13 | infoElement = document.querySelector('.info') as HTMLElement;
14 | startElement = document.querySelector('#start') as HTMLButtonElement;
15 | infoColorElement = document.querySelector('.info__color') as HTMLElement;
16 | precisionElement = document.querySelector('#mode-precision') as HTMLButtonElement;
17 | speedElement = document.querySelector('#mode-speed') as HTMLInputElement;
18 | stepElement = document.querySelector('#step') as HTMLInputElement;
19 | algorithmSimpleElement = document.querySelector('#algorithm-simple') as HTMLInputElement;
20 | algorithmSqrtElement = document.querySelector('#algorithm-sqrt') as HTMLInputElement;
21 | algorithmDominantElement = document.querySelector('#algorithm-dominant') as HTMLInputElement;
22 | width640Element = document.querySelector('#width-640') as HTMLInputElement;
23 | width1280Element = document.querySelector('#width-1280') as HTMLInputElement;
24 | width2560Element = document.querySelector('#width-2560') as HTMLInputElement;
25 |
26 | stoped = false;
27 | isPrecision = true;
28 | timer?: number;
29 | step = 1;
30 | algorithm: 'simple' | 'sqrt' | 'dominant' = 'simple';
31 |
32 | constructor() {
33 | this.bindEvents();
34 | this.start();
35 | }
36 |
37 | bindEvents() {
38 | this.startElement.onclick = () => {
39 | this.stoped = !this.stoped;
40 | if (this.stoped) {
41 | this.startElement.innerText = 'Start';
42 | this.stop();
43 | } else {
44 | this.startElement.innerText = 'Stop';
45 | this.start();
46 | }
47 | };
48 |
49 | this.precisionElement.onclick = () => {
50 | this.isPrecision = true;
51 | this.getColor();
52 | };
53 |
54 | this.speedElement.onclick = () => {
55 | this.isPrecision = false;
56 | this.getColor();
57 | };
58 |
59 | this.stepElement.oninput = () => {
60 | this.step = Number(this.stepElement.value);
61 | this.getColor();
62 | };
63 |
64 | this.algorithmSimpleElement.onclick = () => {
65 | this.algorithm = 'simple';
66 | };
67 |
68 | this.algorithmSqrtElement.onclick = () => {
69 | this.algorithm = 'sqrt';
70 | };
71 |
72 | this.algorithmDominantElement.onclick = () => {
73 | this.algorithm = 'dominant';
74 | };
75 |
76 | this.width640Element.onclick = () => {
77 | this.canvas.width = 640;
78 | this.canvas.height = 480;
79 | };
80 |
81 | this.width1280Element.onclick = () => {
82 | this.canvas.width = 1280;
83 | this.canvas.height = 1024;
84 | };
85 |
86 | this.width2560Element.onclick = () => {
87 | this.canvas.width = 2560;
88 | this.canvas.height = 2048;
89 | };
90 | }
91 |
92 | start() {
93 | this.timer = window.setInterval(this.nextStep.bind(this), 100);
94 | }
95 |
96 | stop() {
97 | clearInterval(this.timer);
98 | }
99 |
100 | nextStep() {
101 | const { width, height } = this.canvas;
102 |
103 | this.ctx.fillStyle = 'rgba(' + [
104 | rndFloor(255),
105 | rndFloor(255),
106 | rndFloor(255),
107 | rnd(1)
108 | ].join(',') + ')';
109 |
110 | this.ctx.fillRect(
111 | rnd(width),
112 | rnd(height),
113 | rnd(width),
114 | rnd(height)
115 | );
116 |
117 | this.getColor();
118 | }
119 |
120 | getColor() {
121 | const timeBefore = Date.now();
122 | const color = fac.getColor(this.canvas, {
123 | algorithm: this.algorithm,
124 | mode: this.isPrecision ? 'precision' : 'speed',
125 | step: this.step,
126 | });
127 |
128 | const time = Date.now() - timeBefore;
129 |
130 | this.infoElement.style.backgroundColor = color.rgba;
131 | this.infoColorElement.innerHTML = [
132 | 'rgb: ' + color.rgb,
133 | 'rgba: ' + color.rgba,
134 | 'hex: ' + color.hex,
135 | 'hexa: ' + color.hexa,
136 | `time: ${time} ms`,
137 | ].map(item => `${item}
`).join('');
138 | }
139 | };
140 |
141 | window.addEventListener('load', () => {
142 | new App();
143 | }, false);
144 |
--------------------------------------------------------------------------------
/src/common.css:
--------------------------------------------------------------------------------
1 | html,
2 | body
3 | {
4 | font-family: Arial;
5 | font-size: 16px;
6 |
7 | margin: 0;
8 | padding: 0;
9 |
10 | border: 0;
11 | background: black;
12 | }
13 |
14 | .nav
15 | {
16 | position: fixed;
17 | z-index: 10000;
18 | top: 5px;
19 | left: 5px;
20 | padding: 10px;
21 |
22 | background: rgba(0, 0, 0, .5);
23 |
24 | border-radius: 5px;
25 | }
26 |
27 | .back
28 | {
29 | margin-right: 15px;
30 | }
31 |
32 | .prev
33 | {
34 | margin-right: 3px;
35 | }
36 |
37 | .button
38 | {
39 | padding: 5px 15px;
40 |
41 | transition: opacity .3s ease;
42 | text-decoration: none;
43 |
44 | color: #fff;
45 | border-radius: 5px;
46 | background: rgba(255, 255, 255, .3);
47 |
48 | font-size: 12px;
49 |
50 | filter: grayscale(100%);
51 | }
52 |
53 | .button:hover
54 | {
55 | opacity: .5;
56 | }
57 |
--------------------------------------------------------------------------------
/src/common.ts:
--------------------------------------------------------------------------------
1 | import { hit } from 'lyam';
2 |
3 | import './common.css';
4 |
5 | window.addEventListener('load', () => {
6 | const pages = [
7 | 'background',
8 | 'timeline',
9 | 'gradient',
10 | 'gradient_stripes',
11 | 'border',
12 | 'gallery',
13 | 'gallery_vertical',
14 | 'box-shadow',
15 | 'box-shadow-4-sides',
16 | 'ambilight',
17 | 'text-photo',
18 | 'canvas',
19 | 'define'
20 | ];
21 |
22 | let prev = pages[pages.length - 1];
23 | let next = pages[1];
24 |
25 | pages.some((item, i) => {
26 | prev = pages[i - 1] || pages[pages.length - 1];
27 | next = pages[i + 1] || pages[0];
28 |
29 | return location.pathname.search('/' + item + '\\.') > -1;
30 | });
31 |
32 | const nav = document.createElement('div');
33 | nav.innerHTML = '\
34 |
🏠\
35 |
◀\
36 |
▶\
37 |
';
38 |
39 | document.body.appendChild(nav);
40 |
41 | hit('49603183');
42 | }, false);
43 |
--------------------------------------------------------------------------------
/src/define/index.css:
--------------------------------------------------------------------------------
1 | .select-file-container
2 | {
3 | margin-top: 50px;
4 | padding: 10px;
5 |
6 | text-align: center;
7 | white-space: nowrap;
8 |
9 | color: white;
10 | background: rgba(0, 0, 0, .5);
11 | }
12 |
13 | .images
14 | {
15 | margin-top: 50px;
16 | }
17 |
18 | .images__item
19 | {
20 | margin: 0 25px 25px 25px;
21 |
22 | transition: all .3s linear;
23 | display: inline-block;
24 |
25 | border-radius: 5px;
26 | }
27 |
28 | .images__img-container
29 | {
30 | padding: 25px;
31 | }
32 |
33 | .images__img
34 | {
35 | max-width: 100%;
36 |
37 | box-sizing: border-box;
38 | border: 1px solid gray;
39 | background: url('../images/transparent.png') 0 0 repeat;
40 | }
41 |
42 | .images__title
43 | {
44 | width: 100%;
45 | padding: 10px;
46 | border-bottom: 1px solid rgba(255, 255, 255, .1);
47 | box-sizing: border-box;
48 |
49 | text-overflow: ellipsis;
50 | overflow-x: hidden;
51 | }
52 |
53 | .images__body
54 | {
55 | padding: 10px;
56 | border-bottom: 1px solid rgba(255, 255, 255, .1);
57 | box-sizing: border-box;
58 | }
59 |
60 | .images__algorithm {
61 | padding: 5px;
62 | display: block;
63 | background: url('../images/transparent.png') 0 0 repeat;
64 | border-radius: 5px;
65 | }
66 |
--------------------------------------------------------------------------------
/src/define/index.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor, FastAverageColorResult } from 'fast-average-color';
2 |
3 | import '../common';
4 | import './index.css';
5 |
6 | const fac = new FastAverageColor();
7 |
8 | class App {
9 | imageCounter = 0;
10 |
11 | constructor() {
12 | const input = document.querySelector('.select-file') as HTMLInputElement;
13 | const captureButton = document.querySelector('.capture-photo') as HTMLButtonElement;
14 |
15 | input.onchange = e => {
16 | const file = (e.target as HTMLInputElement).files?.[0];
17 | const reader = new FileReader();
18 |
19 | reader.onloadend = () => {
20 | const image = new Image();
21 | image.src = reader.result as string;
22 |
23 | this.getColors(image).then(colors => {
24 | this.addImage(image, file?.name || '...', colors);
25 | });
26 | };
27 |
28 | if (file) {
29 | reader.readAsDataURL(file);
30 | }
31 | };
32 |
33 | captureButton.onclick = () => {
34 | this.capture();
35 | };
36 | }
37 |
38 | setImageColor(elem: HTMLLabelElement, backgroundColor: string, isDark: boolean) {
39 | const container = elem.parentNode!.parentNode as HTMLDivElement;
40 | container.style.backgroundColor = backgroundColor;
41 | container.style.color = this.getTextColor(isDark);
42 | }
43 |
44 | getColors(image: HTMLImageElement) {
45 | return Promise.all([
46 | fac.getColorAsync(image, { algorithm: 'simple', mode: 'precision' }),
47 | fac.getColorAsync(image, { algorithm: 'sqrt', mode: 'precision' }),
48 | fac.getColorAsync(image, { algorithm: 'dominant', mode: 'precision' })
49 | ]);
50 | }
51 |
52 | addImage(resource: HTMLImageElement, name: string, colors: FastAverageColorResult[]) {
53 | const images = document.querySelector('.images') as HTMLDivElement;
54 | const item = document.createElement('div');
55 | item.className = 'images__item';
56 | const firstColor = colors[0];
57 | item.style.background = firstColor.rgb;
58 | item.style.color = this.getTextColor(firstColor.isDark);
59 | images.insertBefore(item, images.firstChild);
60 |
61 | const title = document.createElement('div');
62 | title.className = 'images__title';
63 | title.innerText = name;
64 | item.appendChild(title);
65 |
66 | const body = document.createElement('div');
67 | body.className = 'images__body';
68 | body.innerHTML = 'Algorithms:
';
69 | body.appendChild(this.getColorInfo(colors[0], 'simple', true));
70 | body.appendChild(this.getColorInfo(colors[1], 'sqrt', false));
71 | body.appendChild(this.getColorInfo(colors[2], 'dominant', false));
72 |
73 | item.appendChild(body);
74 |
75 | resource.className = 'images__img';
76 |
77 | const container = document.createElement('div');
78 | container.className = 'images__img-container';
79 | container.appendChild(resource);
80 | item.appendChild(container);
81 |
82 | this.imageCounter++;
83 | }
84 |
85 | getTextColor(isDark: boolean) {
86 | return isDark ? 'white' : 'black';
87 | }
88 |
89 | getColorInfo(color: FastAverageColorResult, algorithm: string, checked: boolean) {
90 | const text = [
91 | color.rgb,
92 | color.rgba,
93 | color.hex,
94 | color.hexa
95 | ].join(', ');
96 |
97 | const label = document.createElement('label')
98 | label.className = 'images__algorithm';
99 | label.style.background = color.rgb;
100 | label.style.color = this.getTextColor(color.isDark);
101 |
102 | const input = document.createElement('input');
103 | input.name = 'radio' + this.imageCounter;
104 | input.type = 'radio';
105 | input.checked = checked;
106 | input.onclick = () => {
107 | this.setImageColor(label, color.rgb, color.isDark);
108 | };
109 | label.appendChild(input);
110 | label.appendChild(document.createTextNode(algorithm + ': ' + text))
111 |
112 | return label;
113 | }
114 |
115 | capture() {
116 | // @ts-ignore
117 | navigator.getUserMedia({ video: true, audio: false }, mediaStream => {
118 | // Firefox
119 | if (!('readyState' in mediaStream)) {
120 | // @ts-ignore
121 | mediaStream.readyState = 'live';
122 | }
123 |
124 | const video = document.createElement('video');
125 | const previewStream = new MediaStream(mediaStream);
126 |
127 | if (window.HTMLMediaElement) {
128 | video.srcObject = previewStream; // Safari 11 doesn't allow use of createObjectURL for MediaStream
129 | } else {
130 | // @ts-ignore
131 | video.src = URL.createObjectURL(previewStream);
132 | }
133 |
134 | video.muted = true;
135 | // Required by Safari on iOS 11. See https://webkit.org/blog/6784
136 | video.setAttribute('playsinline', '');
137 | video.play();
138 | video.addEventListener('playing', () => {
139 | setTimeout(() => {
140 | const canvas = document.createElement('canvas');
141 | canvas.width = video.videoWidth;
142 | canvas.height = video.videoHeight;
143 |
144 | const ctx = canvas.getContext('2d');
145 | if (ctx) {
146 | ctx.drawImage(video, 0, 0);
147 | }
148 |
149 | const image = new Image();
150 | image.src = canvas.toDataURL('image/png');
151 |
152 | this.getColors(image).then(colors => {
153 | this.addImage(image, 'Camera', colors);
154 | // @ts-ignore
155 | mediaStream.stop();
156 | });
157 | }, 500);
158 | });
159 | }, () => {
160 | // console.log('failure to get media');
161 | });
162 | }
163 | };
164 |
165 | // @ts-ignore
166 | navigator.getUserMedia = navigator.getUserMedia ||
167 | // @ts-ignore
168 | navigator.webkitGetUserMedia ||
169 | // @ts-ignore
170 | navigator.mozGetUserMedia;
171 |
172 | window.addEventListener('load', () => {
173 | new App();
174 | }, false);
175 |
--------------------------------------------------------------------------------
/src/gallery/index.css:
--------------------------------------------------------------------------------
1 | .big-image {
2 | position: fixed;
3 |
4 | left: 50%;
5 | top: 50%;
6 | max-width: 60%;
7 | width: 640px;
8 | height: auto;
9 |
10 | transform: translate(-50%, -50%);
11 |
12 | transition: opacity 1s ease;
13 | opacity: 1;
14 | }
15 |
16 | .big-image_hidden {
17 | opacity: 0;
18 | }
19 |
20 | .big-image-border {
21 | --width: calc(100vw / 2 - 640px / 2 + .5px);
22 | --height: calc(100vh / 2 - 480px / 2 + .5px);
23 |
24 | position: fixed;
25 |
26 | left: 0;
27 | top: 0;
28 | width: 100%;
29 | height: 100%;
30 | box-sizing: border-box;
31 |
32 | border-left: var(--width) solid transparent;
33 | border-right: var(--width) solid transparent;
34 | border-top: var(--height) solid transparent;
35 | border-bottom: var(--height) solid transparent;
36 |
37 | transition: border-color .5s ease;
38 | }
39 |
40 | @media (max-width: 1066px) {
41 | .big-image-border {
42 | outline: 10px solid red;
43 | --width: calc(100vw / 2 - 60vw / 2 + .5px);
44 | --height: calc(100vh / 2 - 45vw / 2 + .5px);
45 | }
46 | }
47 |
48 | .slider {
49 | position: fixed;
50 |
51 | left: 50%;
52 | bottom: 0;
53 |
54 | transform: translateX(-50%);
55 |
56 | white-space: nowrap;
57 |
58 | background: rgba(255, 255, 255, .2);
59 |
60 | border-radius: 5px 5px 0 0;
61 |
62 | line-height: 0;
63 | }
64 |
65 | .slider__item {
66 | border: 0;
67 |
68 | margin: 5px 5px 5px 0;
69 | width: 80px;
70 | height: 60px;
71 |
72 | cursor: pointer;
73 |
74 | transition: opacity .3s ease;
75 | }
76 |
77 | .slider__item_active {
78 | outline: 5px solid orange;
79 | }
80 |
81 | .slider__item_first {
82 | margin-left: 5px;
83 | }
84 |
85 | .slider__item_last {
86 | border-radius: 0 5px 0 0;
87 | }
88 |
89 | .slider__item:hover {
90 | opacity: .6;
91 | }
92 |
93 | .slider__item_active:hover {
94 | opacity: 1;
95 | }
96 |
--------------------------------------------------------------------------------
/src/gallery/index.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor } from 'fast-average-color';
2 |
3 | import '../common';
4 | import './index.css';
5 |
6 | const fac = new FastAverageColor();
7 |
8 | window.addEventListener('load', () => {
9 | const items = Array.from(document.querySelectorAll('.slider__item'));
10 | const border = document.querySelector('.big-image-border') as HTMLDivElement;
11 | const bigImage = document.querySelector('.big-image') as HTMLImageElement;
12 |
13 | bigImage.classList.remove('big-image_hidden');
14 |
15 | function onClick(elem: HTMLImageElement) {
16 | for (let item of items) {
17 | item.classList.remove('slider__item_active');
18 | }
19 |
20 | elem.classList.add('slider__item_active');
21 |
22 | bigImage.src = elem.src;
23 |
24 | const width = bigImage.naturalWidth;
25 | const height = bigImage.naturalHeight;
26 | const size = 30;
27 |
28 | const top = fac.getColor(elem, {left: 0, top: 0, width: width, height: size});
29 | const bottom = fac.getColor(elem, {left: 0, top: height - size, width: width, height: size});
30 | const left = fac.getColor(elem, {left: 0, top: 0, width: size, height: height});
31 | const right = fac.getColor(elem, {left: width - size, top: 0, width: size, height: height});
32 |
33 | border.style.borderTopColor = top.rgb;
34 | border.style.borderRightColor = right.rgb;
35 | border.style.borderBottomColor = bottom.rgb;
36 | border.style.borderLeftColor = left.rgb;
37 | }
38 |
39 | onClick(items[0]);
40 |
41 | for (let item of items) {
42 | item.onclick = function() {
43 | onClick(this as HTMLImageElement);
44 | };
45 | }
46 | });
47 |
--------------------------------------------------------------------------------
/src/gallery_vertical/getColors.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor } from 'fast-average-color';
2 |
3 | const fac = new FastAverageColor();
4 |
5 | export function getVerticalColors(img: HTMLImageElement) {
6 | const result: Array = [];
7 |
8 | for (let i = 0; i < img.height; i++) {
9 | const color = fac.getColor(img, {
10 | left: 0,
11 | width: img.width,
12 | top: i,
13 | height: 1,
14 | mode: 'precision',
15 | });
16 |
17 | result.push(color.rgba);
18 | }
19 |
20 | return result;
21 | }
22 |
23 | export function getHorizontalColors(img: HTMLImageElement) {
24 | const result: Array = [];
25 |
26 | for (let i = 0; i < img.width; i++) {
27 | const color = fac.getColor(img, {
28 | left: i,
29 | top: 0,
30 | width: 1,
31 | height: img.height,
32 | mode: 'precision',
33 | });
34 |
35 | result.push(color.rgba);
36 | }
37 |
38 | return result;
39 | }
40 |
41 | export function getVerticalGradient(data: Array) {
42 | return `linear-gradient(180deg, ${data.join(', ')})`;
43 | }
44 |
45 | export function getHorizontalGradient(data: Array) {
46 | return `linear-gradient(90deg, ${data.join(', ')})`;
47 | }
48 |
--------------------------------------------------------------------------------
/src/gallery_vertical/index.css:
--------------------------------------------------------------------------------
1 | .big-image-container {
2 | position: fixed;
3 |
4 | left: 50%;
5 | top: 50%;
6 | width: 640px;
7 | height: auto;
8 |
9 | transform: translate(-50%, -50%);
10 |
11 | transition: opacity 1s ease;
12 | opacity: 1;
13 | }
14 |
15 | .big-image {
16 | display: block;
17 | width: 100%;
18 | height: auto;
19 | }
20 |
21 | .vertical-gradient {
22 | position: absolute;
23 |
24 | left: 0;
25 | top: 0;
26 | width: 50px;
27 | height: 100%;
28 | margin-left: -60px;
29 | }
30 |
31 | .horizontal-gradient {
32 | position: absolute;
33 |
34 | left: 0;
35 | top: 0;
36 | width: 100%;
37 | height: 50px;
38 | margin-top: -60px;
39 | }
40 |
41 | .big-image_hidden {
42 | opacity: 0;
43 | }
44 |
45 | .slider {
46 | position: fixed;
47 |
48 | left: 50%;
49 | bottom: 0;
50 |
51 | transform: translateX(-50%);
52 |
53 | white-space: nowrap;
54 |
55 | background: rgba(255, 255, 255, .2);
56 |
57 | border-radius: 5px 5px 0 0;
58 |
59 | line-height: 0;
60 | }
61 |
62 | .slider__item {
63 | border: 0;
64 |
65 | margin: 5px 5px 5px 0;
66 | width: 80px;
67 | height: 60px;
68 |
69 | cursor: pointer;
70 |
71 | transition: opacity .3s ease;
72 | }
73 |
74 | .slider__item_active {
75 | outline: 5px solid orange;
76 | }
77 |
78 | .slider__item_first {
79 | margin-left: 5px;
80 | }
81 |
82 | .slider__item_last {
83 | border-radius: 0 5px 0 0;
84 | }
85 |
86 | .slider__item:hover {
87 | opacity: .6;
88 | }
89 |
90 | .slider__item_active:hover {
91 | opacity: 1;
92 | }
93 |
--------------------------------------------------------------------------------
/src/gallery_vertical/index.ts:
--------------------------------------------------------------------------------
1 | import '../common';
2 | import { getVerticalColors, getVerticalGradient, getHorizontalGradient, getHorizontalColors } from './getColors';
3 | import './index.css';
4 |
5 | window.addEventListener('load', () => {
6 | const items = Array.from(document.querySelectorAll('.slider__item'));
7 | const bigImage = document.querySelector('.big-image') as HTMLImageElement;
8 | const verticalGradient = document.querySelector('.vertical-gradient') as HTMLDivElement;
9 | const horizontalGradient = document.querySelector('.horizontal-gradient') as HTMLDivElement;
10 |
11 | bigImage.classList.remove('big-image_hidden');
12 |
13 | function onClick(elem: HTMLImageElement) {
14 | for (let item of items) {
15 | item.classList.remove('slider__item_active');
16 | }
17 |
18 | elem.classList.add('slider__item_active');
19 |
20 | bigImage.onload = function() {
21 | const verticalColors = getVerticalColors(bigImage);
22 | const horizontalColors = getHorizontalColors(bigImage);
23 | verticalGradient.style.background = getVerticalGradient(verticalColors);
24 | horizontalGradient.style.background = getHorizontalGradient(horizontalColors);
25 | };
26 |
27 | bigImage.src = elem.src;
28 |
29 | }
30 |
31 | onClick(items[0]);
32 |
33 | for (let item of items) {
34 | item.onclick = function() {
35 | onClick(this as HTMLImageElement);
36 | };
37 | }
38 | });
39 |
--------------------------------------------------------------------------------
/src/gradient/index.css:
--------------------------------------------------------------------------------
1 | .item
2 | {
3 | position: relative;
4 |
5 | float: left;
6 |
7 | box-sizing: border-box;
8 | width: 400px;
9 | max-width: 100%;
10 | margin: 1px 1px 0 0;
11 |
12 | transition: all .5s ease;
13 | }
14 |
15 | .item__image
16 | {
17 | position: relative;
18 | }
19 |
20 | img
21 | {
22 | display: block;
23 |
24 | width: 100%;
25 | }
26 |
27 | .item__gradient
28 | {
29 | position: absolute;
30 | z-index: 1;
31 | top: 0;
32 | right: 0;
33 | bottom: 50%;
34 | left: 0;
35 |
36 | transition: all .5s ease;
37 | }
38 |
39 | .item_bottom .item__gradient
40 | {
41 | top: 50%;
42 | bottom: 0;
43 | }
44 |
45 |
46 | .item__text
47 | {
48 | padding: 20px;
49 | }
50 |
--------------------------------------------------------------------------------
/src/gradient/index.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor } from 'fast-average-color';
2 |
3 | import '../common';
4 | import './index.css';
5 |
6 | const fac = new FastAverageColor();
7 |
8 | window.addEventListener('load', () => {
9 | Array.from(document.querySelectorAll('.item')).forEach(item => {
10 | const image = item.querySelector('img') as HTMLImageElement;
11 | const isBottom = item.classList.contains('item_bottom');
12 | const gradient = item.querySelector('.item__gradient') as HTMLDivElement;
13 |
14 | const height = image.naturalHeight;
15 | const size = 50;
16 |
17 | const color = fac.getColor(
18 | image,
19 | isBottom ?
20 | { top: height - size, height: size } :
21 | { height: size }
22 | );
23 | const colorEnd = [...color.value.slice(0, 3), 0].join(',');
24 |
25 | item.style.background = color.rgb;
26 | item.style.color = color.isDark ? 'white' : 'black';
27 |
28 | if (isBottom) {
29 | gradient.style.background = `linear-gradient(to bottom, rgba(${colorEnd}) 0%, ${color.rgba} 100%)`;
30 | } else {
31 | gradient.style.background = `linear-gradient(to top, rgba(${colorEnd}) 0%, ${color.rgba} 100%)`;
32 | }
33 | });
34 | }, false);
35 |
--------------------------------------------------------------------------------
/src/gradient_stripes/index.css:
--------------------------------------------------------------------------------
1 | .items
2 | {
3 | box-sizing: border-box;
4 | padding: 20px;
5 | }
6 |
7 | .item
8 | {
9 | position: relative;
10 |
11 | float: left;
12 |
13 | box-sizing: border-box;
14 | width: 460px;
15 | max-width: 100%;
16 | margin: 0 20px 20px 0;
17 | padding: 30px;
18 |
19 | transition: all .5s ease;
20 | }
21 |
22 | img
23 | {
24 | display: block;
25 |
26 | width: 100%;
27 | }
28 |
29 | .item__text
30 | {
31 | padding: 10px 0 10px 0;
32 | }
33 |
--------------------------------------------------------------------------------
/src/gradient_stripes/index.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor, FastAverageColorResult } from 'fast-average-color';
2 |
3 | import '../common';
4 | import './index.css';
5 |
6 | const fac = new FastAverageColor();
7 |
8 | function getGradient(image: HTMLImageElement, padding = 30, count = 10) {
9 | const naturalHeight = image.naturalHeight;
10 | const height = image.height;
11 |
12 | const naturalHeightPart = Math.floor(naturalHeight / count);
13 | const heightPart = Math.floor(height / count);
14 |
15 | const parts: string[] = [];
16 |
17 | const colors: FastAverageColorResult[] = [];
18 | let value = 'linear-gradient(to bottom, ';
19 |
20 | for (let i = 0; i < count; i++) {
21 | const color = fac.getColor(image, {
22 | left: 0,
23 | top: i * naturalHeightPart,
24 | height: naturalHeightPart
25 | });
26 |
27 | const top = i ? (i * heightPart) + padding : 0;
28 | const bottom = ((i + 1) * heightPart - 1) + padding;
29 |
30 | parts.push(`${color.rgb} ${top}px, ${color.rgb} ${bottom}px`);
31 | colors.push(color);
32 | }
33 |
34 | value += parts.join(', ');
35 | value += ')';
36 |
37 | return {
38 | value,
39 | lastColor: colors[colors.length - 1],
40 | };
41 | }
42 |
43 | function updateStripes(items: HTMLElement[]) {
44 | items.forEach(item => {
45 | const image = item.querySelector('img') as HTMLImageElement;
46 | const { value, lastColor } = getGradient(image);
47 |
48 | item.style.background = value;
49 | item.style.color = lastColor.isDark ? 'white' : 'black';
50 | });
51 | }
52 |
53 | window.addEventListener('load', () => {
54 | const items = Array.from(document.querySelectorAll('.item'));
55 |
56 | window.addEventListener('resize', () => updateStripes(items), false);
57 | updateStripes(items);
58 | }, false);
59 |
--------------------------------------------------------------------------------
/src/text-photo/index.css:
--------------------------------------------------------------------------------
1 | .controls
2 | {
3 | font-size: 11px;
4 | line-height: 14px;
5 |
6 | position: fixed;
7 | z-index: 100;
8 | top: 70px;
9 | left: 10px;
10 |
11 | margin: 0;
12 | padding: 0;
13 |
14 | color: #999;
15 | }
16 |
17 | .controls label
18 | {
19 | display: block;
20 | }
21 |
22 | .controls input
23 | {
24 | font-size: 11px;
25 | line-height: 14px;
26 | }
27 |
28 | .big-photo,
29 | .text-photo
30 | {
31 | position: fixed;
32 | top: 50%;
33 | left: 50%;
34 |
35 | transform: translate(-50%, -50%);
36 | }
37 |
38 | @media (max-width: 900px) and (min-width: 800px)
39 | {
40 | .big-photo,
41 | .text-photo
42 | {
43 | transform: translate(-50%, -50%) scale(.9);
44 | }
45 | }
46 |
47 | @media (max-width: 800px) and (min-width: 700px)
48 | {
49 | .big-photo,
50 | .text-photo
51 | {
52 | transform: translate(-50%, -50%) scale(.8);
53 | }
54 | }
55 |
56 | @media (max-width: 700px) and (min-width: 600px)
57 | {
58 | .big-photo,
59 | .text-photo
60 | {
61 | transform: translate(-50%, -50%) scale(.7);
62 | }
63 | }
64 |
65 | @media (max-width: 600px)
66 | {
67 | .big-photo,
68 | .text-photo
69 | {
70 | transform: translate(-50%, -50%) scale(.6);
71 | }
72 | }
73 |
74 | .text-photo
75 | {
76 | visibility: visible;
77 |
78 | opacity: 0;
79 | }
80 |
81 | .big-photo_fade
82 | {
83 | transition: opacity 2s ease;
84 |
85 | opacity: 0;
86 | }
87 |
88 | .big-photo_load
89 | {
90 | visibility: hidden;
91 | }
92 |
93 | .text-photo_fade
94 | {
95 | transition: opacity 2s ease;
96 |
97 | opacity: 1;
98 | }
99 |
--------------------------------------------------------------------------------
/src/text-photo/index.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor } from 'fast-average-color';
2 |
3 | import '../common';
4 | import './index.css';
5 |
6 | type Browser = 'firefox' | 'ie' | 'chrome' | 'opera';
7 |
8 | const fac = new FastAverageColor();
9 |
10 | class App {
11 | image = document.querySelector('.big-photo') as HTMLImageElement;
12 | canvas = document.querySelector('.text-photo') as HTMLCanvasElement;
13 | ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D;
14 | browser?: Browser;
15 |
16 | constructor() {
17 | Array.from(document.querySelectorAll('input')).forEach(input => {
18 | const that = this;
19 | input.onclick = () => {
20 | that.setImage(input.value as Browser);
21 | };
22 | });
23 |
24 | this.setImage('firefox');
25 | }
26 |
27 | setImage(browser: Browser) {
28 | if (this.browser === browser) { return; }
29 |
30 | this.browser = browser;
31 | this.image.classList.remove('big-photo_fade');
32 | this.image.classList.add('big-photo_load');
33 | this.canvas.classList.remove('text-photo_fade');
34 |
35 | this.image.onload = () => {
36 | const { width, height } = this.image;
37 |
38 | this.canvas.width = width;
39 | this.canvas.height = height;
40 | this.ctx.clearRect(0, 0, width, height);
41 |
42 | setTimeout(() => {
43 | this.generate(this.getBrowserTitle(browser));
44 |
45 | this.image.classList.add('big-photo_fade');
46 | this.image.classList.remove('big-photo_load');
47 | this.canvas.classList.add('text-photo_fade');
48 | }, 50);
49 | };
50 |
51 | this.image.src = `./images/${browser}.jpg`;
52 | }
53 |
54 | getBrowserTitle(browser: Browser) {
55 | return {
56 | firefox: 'Mozilla Firefox',
57 | chrome: 'Google Chrome',
58 | ie: 'Internet Explorer',
59 | opera: 'Opera',
60 | }[browser];
61 | }
62 |
63 | generate(browserTitle: string) {
64 | const { width, height } = this.image;
65 | const x0 = width / 2;
66 | const y0 = height / 2;
67 |
68 | const n = 40;
69 | const a = 12;
70 | const pi = Math.PI;
71 |
72 | let step;
73 | let fs = 6;
74 | let pos = 0;
75 |
76 | for (let angle = pi; angle < 2 * pi * n; angle += step) {
77 | const r = a * angle / 2 / pi;
78 | step = Math.asin((fs - 2) / r) * 2;
79 | const x = x0 + r * Math.cos(angle);
80 | const y = y0 + r * Math.sin(angle);
81 |
82 | if (x < 0 || y < 0 || x > width || y > height) {
83 | continue;
84 | }
85 |
86 | const color = fac.getColor(this.image, {
87 | left: x,
88 | top: y,
89 | width: Math.floor(fs),
90 | height: Math.floor(fs) * 0.6
91 | });
92 |
93 | this.ctx.save();
94 |
95 | this.ctx.font = Math.floor(fs) + 'px Arial';
96 | this.ctx.fillStyle = color.rgb;
97 | if (!browserTitle[pos]) {
98 | pos = 0;
99 | }
100 |
101 | this.ctx.translate(x, y);
102 | this.ctx.rotate(angle);
103 | this.ctx.fillText(browserTitle[pos], 0, 0);
104 | pos++;
105 |
106 | this.ctx.restore();
107 |
108 | fs += 0.005;
109 | }
110 | }
111 | };
112 |
113 | window.addEventListener('load', () => {
114 | new App();
115 | }, false);
116 |
117 |
--------------------------------------------------------------------------------
/src/timeline/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #000;
3 | color: #fff;
4 |
5 | padding: 10px;
6 | }
7 |
8 | .title-container {
9 | position: absolute;
10 | left: 0;
11 | right: 0;
12 | top: 10px;
13 | text-align: center;
14 | }
15 |
16 | .title {
17 | display: inline-block;
18 | margin: 0 auto;
19 | padding: 0;
20 | color: #fff;
21 | font-size: 30px;
22 | font-weight: normal;
23 | line-height: 30px;
24 | background: linear-gradient(90deg, #555, #fff);
25 | background-clip: text;
26 | -webkit-background-clip: text;
27 | -webkit-text-fill-color: transparent;
28 | white-space: nowrap;
29 | }
30 |
31 | .spinner,
32 | .spinner:after {
33 | border-radius: 50%;
34 | width: 50px;
35 | height: 50px;
36 | }
37 |
38 | .spinner {
39 | position: fixed;
40 | left: 50%;
41 | top: 50%;
42 | transform: translate(-50%, -50%);
43 | border-top: 1.1em solid rgba(255, 255, 255, 0.2);
44 | border-right: 1.1em solid rgba(255, 255, 255, 0.2);
45 | border-bottom: 1.1em solid rgba(255, 255, 255, 0.2);
46 | border-left: 1.1em solid #ffffff;
47 | transform: translateZ(0);
48 | animation: spinner 1.1s infinite linear;
49 | }
50 |
51 | @keyframes spinner {
52 | 0% {
53 | transform: rotate(0deg);
54 | }
55 | 100% {
56 | transform: rotate(360deg);
57 | }
58 | }
59 |
60 | .timeline {
61 | margin: 0 0 10px 0;
62 | }
63 |
64 | .timeline__label {
65 | font-size: 12px;
66 | color: #ccc;
67 | }
68 |
69 | .timeline__colors {
70 | width: auto;
71 | line-height: 0;
72 | font-size: 0;
73 | border: 2px solid rgba(255,255,255,0.1);
74 | }
75 |
76 | .timeline__color {
77 | display: inline-block;
78 | width: 1px;
79 | height: 50px;
80 | }
81 |
82 | .progress {
83 | position: fixed;
84 | top: 10px;
85 | right: 10px;
86 | text-align: right;
87 | }
88 |
89 | .demo {
90 | text-align: center;
91 | }
92 |
93 | .radial-demo,
94 | .conic-demo {
95 | display: inline-block;
96 | width: 48vw;
97 | height: 48vw;
98 | margin-right: 10px;
99 | }
100 |
101 | video {
102 | position: fixed;
103 | right: 10px;
104 | bottom: 10px;
105 | border-radius: 10px;
106 | max-width: 400px;
107 | }
108 |
109 | .movie-selector {
110 | margin-top: 50px;
111 | margin-bottom: 10px;
112 | }
113 |
--------------------------------------------------------------------------------
/src/timeline/index.ts:
--------------------------------------------------------------------------------
1 | import { FastAverageColor, FastAverageColorResult } from 'fast-average-color';
2 | import { secsToHMS } from './utils';
3 |
4 | import '../common';
5 | import './index.css';
6 |
7 | const fac = new FastAverageColor();
8 |
9 | const TIMELINE_HEIGHT = 50;
10 |
11 | interface ColorsOfMoviesData {
12 | step: number;
13 | count: number;
14 | duration: number,
15 | averageColors: string[],
16 | averageSqrtColors: string[],
17 | dominantColors: string[],
18 | palette: number[][];
19 | }
20 |
21 | class ColorsOfMovies {
22 | private video: HTMLVideoElement;
23 | private currentSrc: string = '';
24 | private averageTimeline: HTMLCanvasElement;
25 | private dominantTimeline: HTMLCanvasElement;
26 | private radialDemo: HTMLDivElement;
27 | private conicDemo: HTMLDivElement;
28 | private spinner: HTMLDivElement;
29 | private title: HTMLDivElement;
30 | private progress: HTMLDivElement;
31 | private selectMovie: HTMLSelectElement;
32 | private uploadFile: HTMLInputElement;
33 | private originalDocumentTitle = document.title;
34 |
35 | constructor() {
36 | this.video = document.querySelector('video')!;
37 | this.averageTimeline = document.querySelector('.timeline_type_average .timeline__colors')!;
38 | this.dominantTimeline = document.querySelector('.timeline_type_dominant .timeline__colors')!;
39 | this.spinner = document.querySelector('.spinner')!;
40 |
41 | this.radialDemo = document.querySelector('.radial-demo')!;
42 | this.conicDemo = document.querySelector('.conic-demo')!;
43 | this.title = document.querySelector('.title')!;
44 |
45 | this.progress = document.querySelector('.progress')!;
46 |
47 | this.selectMovie = document.querySelector('.movies')!;
48 | this.selectMovie.addEventListener('change', () => {
49 | this.currentSrc = this.selectMovie.selectedOptions[0].value;
50 | this.start(this.currentSrc);
51 | });
52 |
53 | this.uploadFile = document.querySelector('.upload-file')!;
54 |
55 | const that = this;
56 | this.uploadFile.addEventListener('change', function() {
57 | const files = this.files;
58 |
59 | if (files) {
60 | const file = files.item(0);
61 | if (file) {
62 | const src = URL.createObjectURL(file);
63 | that.currentSrc = src;
64 |
65 | that.start(src);
66 | }
67 | }
68 | });
69 |
70 | this.start(this.selectMovie.selectedOptions[0].value);
71 | }
72 |
73 | public start(src: string) {
74 | this.reset();
75 | this.showSpinner();
76 |
77 | if (src) {
78 | this.video.src = src;
79 | this.video.addEventListener('canplay', this.handleCanPlay);
80 | }
81 | }
82 |
83 | private getStep(duration: number) {
84 | let step = Math.ceil(duration / document.documentElement.clientWidth);
85 |
86 | if (step < 1) {
87 | step = 1;
88 | }
89 |
90 | if (step > 10) {
91 | step = 10;
92 | }
93 |
94 | return step;
95 | }
96 |
97 | private handleCanPlay = () => {
98 | this.getColorsFromMovie(this.currentSrc);
99 | this.hideSpinner();
100 | this.video.removeEventListener('canplay', this.handleCanPlay);
101 | }
102 |
103 | private hideSpinner() {
104 | this.spinner.style.display = 'none';
105 | }
106 |
107 | private showSpinner() {
108 | this.spinner.style.display = 'block';
109 | }
110 |
111 | async getColorsFromMovie(src: string) {
112 | const duration = this.video.duration;
113 | const startTime = Date.now();
114 | const step = this.getStep(duration);
115 | const data: ColorsOfMoviesData = {
116 | step,
117 | duration: duration,
118 | count: 0,
119 | averageColors: [],
120 | averageSqrtColors: [],
121 | dominantColors: [],
122 | palette: [],
123 | };
124 |
125 | const width = Math.ceil(duration / step);
126 | this.averageTimeline.width = width;
127 | this.averageTimeline.height = TIMELINE_HEIGHT;
128 | this.dominantTimeline.width = width;
129 | this.dominantTimeline.height = TIMELINE_HEIGHT;
130 |
131 | let x = 0;
132 | for (let i = 0; i < duration; i += step) {
133 | this.video.currentTime = i;
134 | await this.waitForSeek();
135 |
136 | if (src !== this.currentSrc) {
137 | return;
138 | }
139 |
140 | const averageColor = fac.getColor(this.video, { algorithm: 'simple'});
141 | const averageSqrtColor = fac.getColor(this.video, { algorithm: 'sqrt'});
142 | const dominantColor = fac.getColor(this.video, { algorithm: 'dominant' });
143 |
144 | data.averageColors.push(averageColor.rgb);
145 | data.averageSqrtColors.push(averageSqrtColor.rgb);
146 | data.dominantColors.push(dominantColor.rgb);
147 |
148 | this.addColor(this.averageTimeline, averageColor, x);
149 | this.addColor(this.dominantTimeline, dominantColor, x);
150 |
151 | const percents = Math.floor(i / duration * 100) + '%';
152 | this.progress.innerHTML = [
153 | percents + ', step: ' + step + ' s',
154 | secsToHMS(i) + ' / ' + secsToHMS(duration),
155 | Math.floor((Date.now() - startTime) / 1000) + ' s'
156 | ].join('
');
157 |
158 | document.title = percents + ' — ' + this.originalDocumentTitle;
159 |
160 | this.radialDemo.style.background = 'radial-gradient(' + data.dominantColors.join(',') + ')';
161 | this.conicDemo.style.background = 'conic-gradient(' + data.dominantColors.join(',') + ')';
162 |
163 | x++;
164 | }
165 |
166 | this.title.style.background = 'linear-gradient(90deg,' + data.dominantColors.join(',') + ')';
167 | this.title.style.backgroundClip = 'text';
168 | // Fix for Chrome
169 | this.title.style.webkitBackgroundClip = 'text';
170 | this.progress.innerHTML = '';
171 | document.title = this.originalDocumentTitle;
172 |
173 | this.hideVideo();
174 | }
175 |
176 | private reset() {
177 | this.title.style.background = '';
178 | document.title = this.originalDocumentTitle;
179 |
180 | this.radialDemo.style.background = '';
181 | this.conicDemo.style.background = '';
182 |
183 | this.resetCanvas(this.averageTimeline);
184 | this.resetCanvas(this.dominantTimeline);
185 |
186 | this.progress.innerHTML = '';
187 |
188 | this.video.removeEventListener('canplay', this.handleCanPlay);
189 |
190 | this.showVideo();
191 | }
192 |
193 | private resetCanvas(canvas: HTMLCanvasElement) {
194 | const ctx = canvas.getContext('2d');
195 | if (ctx) {
196 | ctx.clearRect(0, 0, canvas.width, canvas.height);
197 | }
198 | }
199 |
200 | private showVideo() {
201 | this.video.style.display = 'block';
202 | }
203 |
204 | private hideVideo() {
205 | this.video.style.display = 'none';
206 | }
207 |
208 | private addColor(
209 | canvas: HTMLCanvasElement,
210 | color: FastAverageColorResult,
211 | x: number,
212 | ) {
213 | const ctx = canvas.getContext('2d');
214 | if (!ctx) return;
215 |
216 | ctx.fillStyle = color.rgba;
217 | ctx.fillRect(x, 0, 1, TIMELINE_HEIGHT);
218 | }
219 |
220 | private waitForSeek() {
221 | return new Promise(resolve => {
222 | this.video.onseeked = () => {
223 | this.video.onseeked = null;
224 | resolve();
225 | }
226 | });
227 | }
228 | }
229 |
230 | new ColorsOfMovies();
231 |
--------------------------------------------------------------------------------
/src/timeline/utils.ts:
--------------------------------------------------------------------------------
1 | function leadZero(num: number) {
2 | return num > 9 ? num : '0' + num;
3 | }
4 |
5 | export function secsToHMS(value: number) {
6 | const hours = Math.floor(value / 3600);
7 | const mins = Math.floor((value - hours * 3600) / 60);
8 | const secs = Math.floor(value - hours * 3600 - mins * 60);
9 |
10 | return [hours, leadZero(mins), leadZero(secs)].join(':');
11 | }
12 |
--------------------------------------------------------------------------------
/text-photo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Get average color for text photo
5 |
6 |
7 |
8 |
9 |
10 |
11 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/timeline.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 🎨 Colors of movies 🎬
5 |
6 |
7 |
8 |
9 |
10 |
11 | Colors of movies
12 | Select or upload a movie
18 |
19 |
20 |
21 |
25 |
29 |
33 |
34 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Basic Options */
4 | // "incremental": true, /* Enable incremental compilation */
5 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
6 | "module": "ES2015", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
7 | "lib": [
8 | "DOM", "ES6"
9 | ], /* Specify library files to be included in the compilation. */
10 | // "allowJs": true, /* Allow javascript files to be compiled. */
11 | // "checkJs": true, /* Report errors in .js files. */
12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
13 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
15 | // "sourceMap": true, /* Generates corresponding '.map' file. */
16 | // "outFile": "./", /* Concatenate and emit output to single file. */
17 | "outDir": "./dist", /* Redirect output structure to the directory. */
18 | "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
19 | // "composite": true, /* Enable project compilation */
20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
21 | // "removeComments": true, /* Do not emit comments to output. */
22 | // "noEmit": true, /* Do not emit outputs. */
23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
26 |
27 | /* Strict Type-Checking Options */
28 | "strict": true, /* Enable all strict type-checking options. */
29 | "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
30 | "strictNullChecks": true, /* Enable strict null checks. */
31 | "strictFunctionTypes": true, /* Enable strict checking of function types. */
32 | "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
33 | "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
34 | "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
35 | "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
36 |
37 | /* Additional Checks */
38 | "noUnusedLocals": true, /* Report errors on unused locals. */
39 | "noUnusedParameters": true, /* Report errors on unused parameters. */
40 | "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
41 | "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
42 |
43 | /* Module Resolution Options */
44 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
45 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
46 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
47 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
48 | // "typeRoots": [], /* List of folders to include type definitions from. */
49 | // "types": [], /* Type declaration files to be included in compilation. */
50 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
51 | "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
52 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
53 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
54 |
55 | /* Source Map Options */
56 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
57 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
58 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
59 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
60 |
61 | /* Experimental Options */
62 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
63 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
64 |
65 | /* Advanced Options */
66 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/videos/1.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fast-average-color/examples/96b6a7d45fa4d1bc335011f1e4cedad0457c65ac/videos/1.mp4
--------------------------------------------------------------------------------
/webgl.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WebGL Cube with Fast Average Color
5 |
14 |
15 |
16 |
17 |
18 |
19 |
236 |
237 |
--------------------------------------------------------------------------------