]>
Commit | Line | Data |
---|---|---|
1 | var color = document.querySelector('.color'), | |
2 | light = document.querySelector('.light'), | |
3 | timeline = document.querySelector('.timeline'), | |
4 | clear = document.querySelector('.clear'), | |
5 | colors = [], | |
6 | selecting = 0, | |
7 | recording = 0, | |
8 | animationTime = 0, | |
9 | lastFrame = 0, | |
10 | t = null, | |
11 | h = 0, | |
12 | s = 100, | |
13 | l = 50, | |
14 | {round, min, max} = Math; | |
15 | ||
16 | function render() { | |
17 | color.style.backgroundColor = `hsl(${h} ${s}% ${l}%)`; | |
18 | light.style.transform = `translateY(-${l*2.2}px)`; | |
19 | } | |
20 | ||
21 | function renderTimeline() { | |
22 | var background = 'linear-gradient(90deg,', | |
23 | last = 0; | |
24 | for (var {h, s, l, t: now} of colors) { | |
25 | background += `hsl(${h} ${s}% ${l}%) ${round(100*last/3000)}%,`; | |
26 | background += `hsl(${h} ${s}% ${l}%) ${round(100*(last+now)/3000)}%,`; | |
27 | last += now; | |
28 | } | |
29 | background += `black ${round(100*last/3000)}%, black 101%)`; | |
30 | timeline.style.background = background; | |
31 | } | |
32 | ||
33 | function length() { | |
34 | return colors.map((c) => c.t).reduce((s, t) => s + t, 0); | |
35 | } | |
36 | ||
37 | function addColor() { | |
38 | t = Date.now(); | |
39 | colors.push({h, s, l, t: 0}); | |
40 | } | |
41 | ||
42 | function record() { | |
43 | if (recording) setTimeout(record, 100); | |
44 | var c = colors[colors.length - 1], | |
45 | l = length(); | |
46 | if (c.h !== h || c.s !== s || c.l !== l) { | |
47 | c.t = min(3000, Date.now() - t); | |
48 | addColor(); | |
49 | c = colors[colors.length - 1]; | |
50 | } | |
51 | if (l >= 3000) return; | |
52 | c.t = min(3000, Date.now() - t); | |
53 | renderTimeline(); | |
54 | } | |
55 | ||
56 | function preview(current) { | |
57 | if (selecting || recording) return; | |
58 | window.requestAnimationFrame(preview); | |
59 | var dt = current - lastFrame, | |
60 | last = 0; | |
61 | if (dt > 32) { | |
62 | animationTime = (animationTime + dt) % 3000 | |
63 | color.style.background = `hsl(0 0% 0%)`; | |
64 | for (var {h, s, l, t: now} of colors) { | |
65 | if (animationTime >= last && animationTime < last+now) { | |
66 | color.style.background = `hsl(${h} ${s}% ${l}%)`; | |
67 | break; | |
68 | } | |
69 | last += now; | |
70 | } | |
71 | lastFrame = current; | |
72 | } | |
73 | } | |
74 | ||
75 | color.addEventListener('mousemove', ({offsetX: x, offsetY: y}) => { | |
76 | h = round(360 * x / 200); | |
77 | s = round(100 * (200 - y) / 200); | |
78 | render(); | |
79 | }); | |
80 | ||
81 | color.addEventListener('wheel', ({deltaY: y}) => { | |
82 | selecting = 1; | |
83 | l = min(max(0, l + y/4), 100) | |
84 | render(); | |
85 | }); | |
86 | ||
87 | color.addEventListener('mousedown', () => { | |
88 | addColor(); | |
89 | recording = 1; | |
90 | setTimeout(record, 100); | |
91 | }); | |
92 | ||
93 | color.addEventListener('mouseup', () => { | |
94 | recording = 0; | |
95 | }); | |
96 | ||
97 | color.addEventListener('mouseenter', () => { | |
98 | selecting = 1; | |
99 | }); | |
100 | ||
101 | color.addEventListener('mouseout', () => { | |
102 | recording = 0; | |
103 | selecting = 0; | |
104 | window.requestAnimationFrame(preview); | |
105 | }); | |
106 | ||
107 | clear.addEventListener('click', () => { | |
108 | colors = []; | |
109 | renderTimeline(); | |
110 | }); | |
111 | ||
112 | render(); | |
113 | renderTimeline(); |