PairDrop/public/scripts/worker/canvas-worker.js

141 lines
No EOL
3.6 KiB
JavaScript

self.onmessage = (e) => {
switch (e.data.type) {
case "createCanvas": createCanvas(e.data);
break;
case "initCanvas": initCanvas(e.data.footerOffsetHeight, e.data.clientWidth, e.data.clientHeight);
break;
case "startAnimation": startAnimation();
break;
case "onShareModeChange": onShareModeChange(e.data.active);
break;
case "switchAnimation": switchAnimation(e.data.animate);
break;
}
};
let baseColorNormal;
let baseColorShareMode;
let baseOpacityNormal;
let baseOpacityShareMode;
let speed;
let fps;
let c;
let cCtx;
let x0, y0, w, h, dw, offset;
let startTime;
let animate = true;
let currentFrame = 0;
let lastFrame;
let baseColor;
let baseOpacity;
function createCanvas(data) {
baseColorNormal = data.baseColorNormal;
baseColorShareMode = data.baseColorShareMode;
baseOpacityNormal = data.baseOpacityNormal;
baseOpacityShareMode = data.baseOpacityShareMode;
speed = data.speed;
fps = data.fps;
c = data.canvas;
cCtx = c.getContext("2d");
lastFrame = fps / speed - 1;
baseColor = baseColorNormal;
baseOpacity = baseOpacityNormal;
}
function initCanvas(footerOffsetHeight, clientWidth, clientHeight) {
let oldW = w;
let oldH = h;
let oldOffset = offset;
w = clientWidth;
h = clientHeight;
offset = footerOffsetHeight - 28;
if (oldW === w && oldH === h && oldOffset === offset) return; // nothing has changed
c.width = w;
c.height = h;
x0 = w / 2;
y0 = h - offset;
dw = Math.round(Math.min(Math.max(0.6 * w, h)) / 10);
drawFrame(currentFrame);
}
function startAnimation() {
startTime = Date.now();
animateBg();
}
function switchAnimation(state) {
if (!animate && state) {
// animation starts again. Set startTime to specific value to prevent frame jump
startTime = Date.now() - 1000 * currentFrame / fps;
}
animate = state;
requestAnimationFrame(animateBg);
}
function onShareModeChange(active) {
baseColor = active ? baseColorShareMode : baseColorNormal;
baseOpacity = active ? baseOpacityShareMode : baseOpacityNormal;
drawFrame(currentFrame);
}
function drawCircle(ctx, radius) {
ctx.lineWidth = 2;
let opacity = Math.max(0, baseOpacity * (1 - 1.2 * radius / Math.max(w, h)));
if (radius > dw * 7) {
opacity *= (8 * dw - radius) / dw
}
if (ctx.setStrokeColor) {
// older blink/webkit based browsers do not understand opacity in strokeStyle. Use deprecated setStrokeColor instead
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/strokeStyle#webkitblink-specific_note
ctx.setStrokeColor("grey", opacity);
}
else {
ctx.strokeStyle = `rgb(${baseColor} / ${opacity})`;
}
ctx.beginPath();
ctx.arc(x0, y0, radius, 0, 2 * Math.PI);
ctx.stroke();
}
function drawCircles(ctx, frame) {
ctx.clearRect(0, 0, w, h);
for (let i = 7; i >= 0; i--) {
drawCircle(ctx, dw * i + speed * dw * frame / fps + 33);
}
}
function drawFrame(frame) {
cCtx.clearRect(0, 0, w, h);
drawCircles(cCtx, frame);
}
function animateBg() {
let now = Date.now();
if (!animate && currentFrame === lastFrame) {
// Animation stopped and cycle finished -> stop drawing frames
return;
}
let timeSinceLastFullCycle = (now - startTime) % (1000 / speed);
let nextFrame = Math.trunc(fps * timeSinceLastFullCycle / 1000);
// Only draw frame if it differs from current frame
if (nextFrame !== currentFrame) {
drawFrame(nextFrame);
currentFrame = nextFrame;
}
requestAnimationFrame(animateBg);
}