var color = document.querySelector('.color'), light = document.querySelector('.light'), timeline = document.querySelector('.timeline'), clear = document.querySelector('.clear'), colors = [], selecting = 0, recording = 0, animationTime = 0, lastFrame = 0, t = null, h = 0, s = 100, l = 50, {round, min, max} = Math; function render() { color.style.backgroundColor = `hsl(${h} ${s}% ${l}%)`; light.style.transform = `translateY(-${l*2.2}px)`; } function renderTimeline() { var background = 'linear-gradient(90deg,', last = 0; for (var {h, s, l, t: now} of colors) { background += `hsl(${h} ${s}% ${l}%) ${round(100*last/3000)}%,`; background += `hsl(${h} ${s}% ${l}%) ${round(100*(last+now)/3000)}%,`; last += now; } background += `black ${round(100*last/3000)}%, black 101%)`; timeline.style.background = background; } function length() { return colors.map((c) => c.t).reduce((s, t) => s + t, 0); } function addColor() { t = Date.now(); colors.push({h, s, l, t: 0}); } function record() { if (recording) setTimeout(record, 100); var c = colors[colors.length - 1], l = length(); if (c.h !== h || c.s !== s || c.l !== l) { c.t = min(3000, Date.now() - t); addColor(); c = colors[colors.length - 1]; } if (l >= 3000) return; c.t = min(3000, Date.now() - t); renderTimeline(); } function preview(current) { if (selecting || recording) return; window.requestAnimationFrame(preview); var dt = current - lastFrame, last = 0; if (dt > 32) { animationTime = (animationTime + dt) % 3000 color.style.background = `hsl(0 0% 0%)`; for (var {h, s, l, t: now} of colors) { if (animationTime >= last && animationTime < last+now) { color.style.background = `hsl(${h} ${s}% ${l}%)`; break; } last += now; } lastFrame = current; } } color.addEventListener('mousemove', ({offsetX: x, offsetY: y}) => { h = round(360 * x / 200); s = round(100 * (200 - y) / 200); render(); }); color.addEventListener('wheel', ({deltaY: y}) => { selecting = 1; l = min(max(0, l + y/4), 100) render(); }); color.addEventListener('mousedown', () => { addColor(); recording = 1; setTimeout(record, 100); }); color.addEventListener('mouseup', () => { recording = 0; }); color.addEventListener('mouseenter', () => { selecting = 1; }); color.addEventListener('mouseout', () => { recording = 0; selecting = 0; window.requestAnimationFrame(preview); }); clear.addEventListener('click', () => { colors = []; renderTimeline(); }); render(); renderTimeline();