react-snow-overlay
Version:
A performant snowfall effect for your website using canvas and web workers
176 lines (175 loc) • 6.79 kB
JavaScript
"use client";
var T = (e) => {
throw TypeError(e);
};
var O = (e, t, n) => t.has(e) || T("Cannot " + n);
var U = (e, t, n) => (O(e, t, "read from private field"), n ? n.call(e) : t.get(e)), A = (e, t, n) => t.has(e) ? T("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(e) : t.set(e, n), x = (e, t, n, r) => (O(e, t, "write to private field"), r ? r.call(e, n) : t.set(e, n), n), a = (e, t, n) => (O(e, t, "access private method"), n);
import { jsx as g } from "react/jsx-runtime";
import { useRef as p, useCallback as I, useEffect as m, memo as L, useReducer as F, useMemo as M } from "react";
const q = Object.freeze({
color: "rgba(255, 255, 255, 0.8)",
maxParticles: 50,
speed: "DEFAULT"
}), H = {
DEFAULT: 33,
FAST: 20,
FASTER: 10
}, _ = 2 ** 31 - 1, N = 50, R = `(function(){"use strict";const D=Object.freeze({color:"rgba(255, 255, 255, 0.8)",maxParticles:50,speed:"DEFAULT"}),S={DEFAULT:33,FAST:20,FASTER:10},x=e=>e.type===0,A=e=>e.type===1,w=e=>e.type===2,P=e=>e.type===3,F=e=>e.type===4,O=e=>typeof e=="object"?e.msBetweenUpdates:S[e];let o={...D},a,h=0,y=-1/0,l=null,c,i,d=!1;self.onmessage=e=>{const{data:s}=e;if(x(s))c=s.canvas,i=c.getContext("2d"),c.width=s.width,c.height=s.height,o={...D,...s.options},a=Array.from({length:o.maxParticles},M);else if(A(s))c.width=s.width,c.height=s.height;else if(P(s)){const{type:m,...r}=s;o={...o,...r.options},T(o.maxParticles)}else if(w(s)){d=!0;const{width:m,height:r}=i.canvas;i.clearRect(0,0,m,r),l&&cancelAnimationFrame(l);return}else F(s)&&(d=!1);const p=()=>{const m=O(o.speed);if(performance.now()-y<m){l=requestAnimationFrame(p);return}const{width:r,height:f}=i.canvas;i.clearRect(0,0,r,f),i.fillStyle=o.color,i.beginPath();for(let n=0;n<o.maxParticles;n++){const t=a[n];i.moveTo(t.x,t.y),i.arc(t.x,t.y,t.r,0,Math.PI*2,!0)}i.fill(),h=(h+.01)%360;const g=Math.sin(h),U=Math.cos(h);for(let n=0;n<o.maxParticles;n++){const t=a[n],W=U*t.cosD-g*t.sinD;if(t.y+=W+1+t.r/2,t.x+=g*2,t.x>r+5||t.x<-5||t.y>f)if(n%3>0)a[n]={x:Math.random()*r,y:-10,r:t.r,d:t.d,sinD:t.sinD,cosD:t.cosD};else{const u=Math.random()*r;g>0?a[n]={x:-5-u,y:Math.random()*f,r:t.r,d:t.d,sinD:t.sinD,cosD:t.cosD}:a[n]={x:r+5+u,y:Math.random()*f,r:t.r,d:t.d,sinD:t.sinD,cosD:t.cosD}}}y=performance.now(),d||(l=requestAnimationFrame(p))};l&&cancelAnimationFrame(l),d||requestAnimationFrame(p)};const T=e=>{const s=a.length;if(e!==s){if(e<s)return a.splice(e,s-e);a.push(...Array.from({length:e-s},M))}},M=()=>{const e=Math.random()*o.maxParticles;return{x:Math.random()*c.width,y:Math.random()*-c.height,r:Math.random()*4+1,d:e,sinD:Math.sin(e),cosD:Math.cos(e)}}})();
`, v = typeof self < "u" && self.Blob && new Blob([R], { type: "text/javascript;charset=utf-8" });
function C(e) {
let t;
try {
if (t = v && (self.URL || self.webkitURL).createObjectURL(v), !t) throw "";
const n = new Worker(t, {
name: e == null ? void 0 : e.name
});
return n.addEventListener("error", () => {
(self.URL || self.webkitURL).revokeObjectURL(t);
}), n;
} catch {
return new Worker(
"data:text/javascript;charset=utf-8," + encodeURIComponent(R),
{
name: e == null ? void 0 : e.name
}
);
} finally {
t && (self.URL || self.webkitURL).revokeObjectURL(t);
}
}
var u = /* @__PURE__ */ ((e) => (e[e.INIT = 0] = "INIT", e[e.UPDATE_SIZE = 1] = "UPDATE_SIZE", e[e.STOP = 2] = "STOP", e[e.UPDATE_OPTIONS = 3] = "UPDATE_OPTIONS", e[e.RESUME = 4] = "RESUME", e))(u || {}), d, c, l;
class k {
constructor(t) {
A(this, c);
A(this, d);
x(this, d, t);
}
init(t) {
a(this, c, l).call(this, {
...t,
type: u.INIT
}, [t.canvas]);
}
updateSize(t) {
a(this, c, l).call(this, {
...t,
type: u.UPDATE_SIZE
});
}
stop() {
a(this, c, l).call(this, {
type: u.STOP
});
}
resume() {
a(this, c, l).call(this, {
type: u.RESUME
});
}
updateOptions(t) {
a(this, c, l).call(this, {
...t,
type: u.UPDATE_OPTIONS
});
}
terminate() {
U(this, d).terminate();
}
}
d = new WeakMap(), c = new WeakSet(), l = function(t, n) {
U(this, d).postMessage(t, {
transfer: n
});
};
const z = (e, t) => {
const n = p(null), r = I(
(...o) => {
n.current !== null && clearTimeout(n.current), n.current = window.setTimeout(() => {
e(...o);
}, t);
},
[e, t]
);
return m(
() => () => void (n.current !== null && clearTimeout(n.current))
), r;
}, j = (e, t) => {
const n = p(null);
return (!n.current || !S(t, n.current.key)) && (n.current = { key: t, value: e() }), n.current.value;
}, S = (e, t) => {
if (e === t) return !0;
if (e === null || t === null || typeof e != "object" || typeof t != "object")
return !1;
const n = Object.keys(e), r = Object.keys(t);
return !(n.length !== r.length || n.some(
(o) => !r.includes(o) || !S(e[o], t[o])
));
}, V = L(function({
zIndex: t = _,
disabled: n,
disabledOnSingleCpuDevices: r,
...o
}) {
const w = p(null), f = p(null), i = p(null), [P, b] = F(
() => !0,
!1
), E = j(() => o, [o]), D = z(() => {
var s;
if (!(typeof window > "u" || // If browser doesn't support OffscreenCanvas, don't bother
!((s = w.current) != null && s.transferControlToOffscreen))) {
if (f.current ?? (f.current = w.current.transferControlToOffscreen()), !i.current) {
i.current = new k(new C()), b();
const y = f.current;
return i.current.init({
canvas: y,
width: window.innerWidth,
height: window.innerHeight,
options: E
});
}
i.current.updateSize({
width: window.innerWidth,
height: window.innerHeight
});
}
}, N);
m(() => (D(), window.addEventListener("resize", D), () => window.removeEventListener("resize", D)), [D]);
const h = M(() => {
const s = !!n, y = r && typeof navigator < "u" && navigator.hardwareConcurrency === 1;
return s || y;
}, [r, n]);
return m(() => {
if (!(!P || !f.current || !i.current)) {
if (h) return i.current.stop();
i.current.updateOptions({ options: E });
}
}, [h, E, P]), m(() => {
var s;
(s = i.current) == null || s[h ? "stop" : "resume"]();
}, [h]), m(() => {
var s;
return (s = i.current) == null ? void 0 : s.terminate;
}, []), /* @__PURE__ */ g(
"canvas",
{
"aria-hidden": "true",
ref: w,
style: {
zIndex: t,
pointerEvents: "none",
userSelect: "none",
position: "fixed",
top: 0,
right: 0,
bottom: 0,
left: 0,
...h && { display: "none" }
}
}
);
});
export {
q as DEFAULT_SNOW_OPTIONS,
H as SNOW_OPTIONS_SPEED_MAP,
V as SnowOverlay
};