rastrillo / fiesta Public

Clone
git clone https://amadan.net/rastrillo/fiesta

Plain git — no account needed to clone.

Download

Download this file

1/*! Fiesta 1.1.0 | MPL-2.0 | https://fiesta.rastrillo.org */
2(function () {
3 "use strict";
4 var root = document.documentElement;
5 if (!window.matchMedia || !window.matchMedia("all").addEventListener) return;
6 var reduce = matchMedia("(prefers-reduced-motion: reduce)");
7 var forced = matchMedia("(forced-colors: active)");
8
9 var FOLLOW = false;
10 var WRITE_MS = 200;
11 var BTN = "[rst-btn],.rst-btn";
12 var CARD = "[rst-box],.rst-box,[rst-card],.rst-card,[rst-stats],.rst-stats,[rst-list],.rst-list";
13 var LIGHT_T = 125, CARD_T = 320, LIT_T = 400;
14
15 var on = false, tx = 0, ty = -1, lx = 0, ly = -1, wx = 0, wy = -1;
16 var raf = 0, last = 0, pending = null, pointer = false, hold = false, force = false, wrote = 0;
17 var cards = new WeakMap(), live = new Set(), timers = new Set(), clock = 0;
18
19 function sun() {
20 var d = new Date(), h = d.getHours() + d.getMinutes() / 60;
21 var t = Math.max(-1, Math.min(1, (h - 12) / 6));
22 return [t * 0.8, -1 + Math.abs(t) * 0.35];
23 }
24 function target() {
25 if (hold || (pointer && FOLLOW)) return;
26 var s = sun();
27 tx = s[0];
28 ty = s[1];
29 }
30 function go() {
31 if (!on || document.hidden) return;
32 if (!raf) raf = requestAnimationFrame(step);
33 }
34 function ease(v, to, dt, T) {
35 return v + (to - v) * (1 - Math.exp(-dt / T));
36 }
37 function light(el, x, y) {
38 var o = cards.get(el);
39 if (!o) { o = { x: x, y: y, l: 0 }; cards.set(el, o); }
40 o.tx = x; o.ty = y; o.tl = 1;
41 live.add(el);
42 }
43 function dim(el) {
44 var o = cards.get(el);
45 if (o) { o.tl = 0; live.add(el); }
46 }
47
48 function step(now) {
49 raf = 0;
50 var dt = last ? Math.min(100, now - last) : 16;
51 last = now;
52 var moving = false;
53 if (pending) {
54 var e = pending;
55 pending = null;
56 var b = e.target.closest && e.target.closest(BTN);
57 var c = e.target.closest && e.target.closest(CARD);
58 var br = b && b.getBoundingClientRect();
59 var cr = c && c.getBoundingClientRect();
60 if (b) {
61 b.style.setProperty("--rst-mx", (e.clientX - br.left).toFixed(0) + "px");
62 b.style.setProperty("--rst-my", (e.clientY - br.top).toFixed(0) + "px");
63 }
64 live.forEach(function (k) { if (k !== c) dim(k); });
65 if (c) light(c, e.clientX - cr.left, e.clientY - cr.top);
66 }
67 if (!hold) {
68 lx = ease(lx, tx, dt, LIGHT_T);
69 ly = ease(ly, ty, dt, LIGHT_T);
70 }
71 if (force || ((Math.abs(lx - wx) > 0.01 || Math.abs(ly - wy) > 0.01) && now - wrote >= WRITE_MS)) {
72 force = false;
73 wx = lx; wy = ly; wrote = now;
74 root.style.setProperty("--rst-light-x", lx.toFixed(3));
75 root.style.setProperty("--rst-light-y", ly.toFixed(3));
76 }
77 if (Math.abs(tx - lx) > 0.002 || Math.abs(ty - ly) > 0.002) moving = true;
78 live.forEach(function (el) {
79 if (!el.isConnected) { live.delete(el); return; }
80 var o = cards.get(el);
81 o.x = ease(o.x, o.tx, dt, CARD_T);
82 o.y = ease(o.y, o.ty, dt, CARD_T);
83 o.l = ease(o.l, o.tl, dt, LIT_T);
84 var done = Math.abs(o.tx - o.x) < 0.5 && Math.abs(o.ty - o.y) < 0.5 && Math.abs(o.tl - o.l) < 0.004;
85 if (done) { o.x = o.tx; o.y = o.ty; o.l = o.tl; }
86 if (!done || !o.still) {
87 el.style.setProperty("--rst-card-x", o.x.toFixed(1) + "px");
88 el.style.setProperty("--rst-card-y", o.y.toFixed(1) + "px");
89 el.style.setProperty("--rst-card-lit", o.l.toFixed(3));
90 }
91 o.still = done;
92 if (!done) moving = true; else if (o.tl === 0) live.delete(el);
93 });
94 if (moving) go(); else last = 0;
95 }
96
97 function onMove(e) {
98 if (e.pointerType !== "mouse") return;
99 if (FOLLOW && !hold) {
100 pointer = true;
101 tx = Math.max(-1, Math.min(1, (e.clientX / innerWidth - 0.5) * 2));
102 ty = Math.max(-1, Math.min(1, (e.clientY / innerHeight - 0.5) * 2)) * 0.6 - 0.4;
103 }
104 pending = e;
105 go();
106 }
107 function onDown(e) {
108 var b = e.target.closest && e.target.closest(BTN);
109 var c = e.pointerType !== "mouse" && e.target.closest && e.target.closest(CARD);
110 var br = b && b.getBoundingClientRect();
111 var cr = c && c.getBoundingClientRect();
112 if (b) {
113 b.style.setProperty("--rst-px", (e.clientX - br.left).toFixed(0) + "px");
114 b.style.setProperty("--rst-py", (e.clientY - br.top).toFixed(0) + "px");
115 }
116 if (e.pointerType === "mouse") return;
117 leavePointer();
118 if (c) {
119 light(c, e.clientX - cr.left, e.clientY - cr.top);
120 var id = setTimeout(function () { timers.delete(id); dim(c); go(); }, 1400);
121 timers.add(id);
122 go();
123 }
124 }
125 function leavePointer() {
126 pointer = false;
127 target();
128 go();
129 }
130 function onLeave() {
131 pending = null;
132 live.forEach(dim);
133 leavePointer();
134 }
135 function onVisibility() {
136 if (document.hidden) {
137 if (raf) cancelAnimationFrame(raf);
138 raf = 0; last = 0;
139 } else {
140 go();
141 }
142 }
143 function onHook(e) {
144 var d = e.detail || {};
145 hold = !!d.hold;
146 if (typeof d.x === "number") { tx = lx = d.x; }
147 if (typeof d.y === "number") { ty = ly = d.y; }
148 force = true;
149 go();
150 }
151
152 function start() {
153 if (on) return;
154 on = true;
155 addEventListener("pointermove", onMove, { passive: true });
156 addEventListener("pointerdown", onDown, { passive: true });
157 addEventListener("keydown", leavePointer, { passive: true });
158 document.addEventListener("pointerleave", onLeave);
159 document.addEventListener("visibilitychange", onVisibility);
160 document.addEventListener("rst:light", onHook);
161 clock = setInterval(function () { target(); go(); }, 5 * 60 * 1000);
162 target();
163 lx = tx; ly = ty; force = true;
164 go();
165 }
166 function stop() {
167 if (!on) return;
168 on = false;
169 removeEventListener("pointermove", onMove);
170 removeEventListener("pointerdown", onDown);
171 removeEventListener("keydown", leavePointer);
172 document.removeEventListener("pointerleave", onLeave);
173 document.removeEventListener("visibilitychange", onVisibility);
174 document.removeEventListener("rst:light", onHook);
175 clearInterval(clock);
176 timers.forEach(clearTimeout);
177 timers.clear();
178 if (raf) cancelAnimationFrame(raf);
179 raf = 0; last = 0; pending = null; pointer = false; hold = false; force = false; wrote = 0;
180 lx = wx = tx = 0; ly = wy = ty = -1;
181 live.clear();
182 cards = new WeakMap();
183 root.style.removeProperty("--rst-light-x");
184 root.style.removeProperty("--rst-light-y");
185 var written = document.querySelectorAll('[style*="--rst-"]');
186 for (var i = 0; i < written.length; i++) {
187 ["--rst-mx", "--rst-my", "--rst-px", "--rst-py", "--rst-card-x", "--rst-card-y", "--rst-card-lit"].forEach(function (p) {
188 written[i].style.removeProperty(p);
189 });
190 }
191 }
192 function settle() {
193 if (reduce.matches || forced.matches) stop(); else start();
194 }
195 reduce.addEventListener("change", settle);
196 forced.addEventListener("change", settle);
197 settle();
198})();
199