@arwes/bgs
Version:
Futuristic Sci-Fi UI Web Framework
129 lines (128 loc) • 4.19 kB
JavaScript
import { createAnimation } from '@arwes/animated';
const defaultProps = {
lineWidth: 1,
lineColor: '#777',
horizontalLineDash: [4],
verticalLineDash: [],
distance: 30
};
const createBackgroundGridLines = (props) => {
const { canvas, animator } = props;
const ctx = canvas.getContext('2d');
if (!ctx) {
return { cancel: () => { } };
}
let resizeObserver;
let transitionControl;
let unsubscribe;
const getSettings = () => ({
...defaultProps,
...props.settingsRef.current
});
const resize = () => {
const dpr = Math.min(window.devicePixelRatio || 2, 2);
const { width, height } = canvas.getBoundingClientRect();
if (canvas.width !== width * dpr || canvas.height !== height * dpr) {
canvas.width = width * dpr;
canvas.height = height * dpr;
}
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.scale(dpr, dpr);
};
const draw = () => {
const { lineWidth, lineColor, horizontalLineDash, verticalLineDash, distance } = getSettings();
const { width, height } = canvas;
const xLength = 1 + Math.floor(width / distance);
const yLength = 1 + Math.floor(height / distance);
const xMargin = width % distance;
const yMargin = height % distance;
ctx.clearRect(0, 0, width, height);
ctx.lineWidth = lineWidth;
ctx.strokeStyle = lineColor;
ctx.setLineDash(horizontalLineDash);
for (let yIndex = 0; yIndex < yLength; yIndex++) {
const y = yMargin / 2 + yIndex * distance;
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(width, y);
ctx.stroke();
ctx.closePath();
}
ctx.setLineDash(verticalLineDash);
for (let xIndex = 0; xIndex < xLength; xIndex++) {
const x = xMargin / 2 + xIndex * distance;
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, height);
ctx.stroke();
ctx.closePath();
}
};
const setup = () => {
if (typeof window !== 'undefined' && !resizeObserver) {
resizeObserver = new window.ResizeObserver(() => {
resize();
draw();
});
resizeObserver.observe(canvas);
resize();
}
};
const stop = () => {
resizeObserver?.disconnect();
resizeObserver = undefined;
transitionControl?.cancel();
transitionControl = undefined;
};
const start = () => {
if (!animator) {
setup();
draw();
canvas.style.opacity = '1';
return;
}
unsubscribe = animator.subscribe((node) => {
switch (node.state) {
case 'entering': {
setup();
draw();
transitionControl = createAnimation({
duration: node.settings.duration.enter,
onUpdate(progress) {
canvas.style.opacity = String(progress);
}
});
break;
}
case 'entered': {
setup();
draw();
canvas.style.opacity = '1';
break;
}
case 'exiting': {
transitionControl = createAnimation({
duration: node.settings.duration.exit,
onUpdate(progress) {
canvas.style.opacity = String(1 - progress);
}
});
break;
}
case 'exited': {
stop();
canvas.style.opacity = '0';
break;
}
}
});
};
const cancel = () => {
unsubscribe?.();
stop();
canvas.style.opacity = '0';
};
start();
return Object.freeze({ cancel });
};
export { createBackgroundGridLines };