Skip to content

Instantly share code, notes, and snippets.

@gaurangrshah
Created February 16, 2024 05:04
Show Gist options
  • Save gaurangrshah/52d0e147a5b605e7b849f26fed51b95c to your computer and use it in GitHub Desktop.
Save gaurangrshah/52d0e147a5b605e7b849f26fed51b95c to your computer and use it in GitHub Desktop.
canvas starfield react
'use client';
// modified slightly from: https://medium.com/designly/how-to-create-an-animated-space-stars-background-effect-in-react-next-js-2806b630379c
import { useEffect, useRef } from 'react';
interface Props {
speedFactor?: number;
backgroundColor?: string;
starColor?: [number, number, number];
starCount?: number;
}
export default function StarfieldCanvas(props: Props) {
const {
speedFactor = 0.05,
backgroundColor = 'black',
starColor = [255, 255, 255],
starCount = 5000,
} = props;
const canvasRef = useRef(null);
useEffect(() => {
const canvas1 = canvasRef.current as HTMLCanvasElement | null;
if (!canvas1) return;
const c = canvas1?.getContext('2d');
if (c) {
const canvasParent = canvas1.parentElement;
let w = window.innerWidth;
let h = window.innerHeight;
const setCanvasExtents = () => {
canvas1.width = w;
canvas1.height = h;
};
setCanvasExtents();
window.onresize = () => {
setCanvasExtents();
};
const makeStars = (count: number) => {
const out = [];
for (let i = 0; i < count; i++) {
const s = {
x: Math.random() * 1600 - 800,
y: Math.random() * 900 - 450,
z: Math.random() * 1000,
};
out.push(s);
}
return out;
};
let stars = makeStars(starCount);
const clear = () => {
c.fillStyle = backgroundColor;
c.fillRect(0, 0, canvas1.width, canvas1.height);
};
const putPixel = (x: number, y: number, brightness: number) => {
const rgb =
'rgba(' +
starColor[0] +
',' +
starColor[1] +
',' +
starColor[2] +
',' +
brightness +
')';
c.fillStyle = rgb;
c.fillRect(x, y, 2.5, 2.5);
};
const moveStars = (distance: number) => {
const count = stars.length;
for (var i = 0; i < count; i++) {
const s = stars[i];
s.z -= distance;
while (s.z <= 1) {
s.z += 1000;
}
}
};
let prevTime: number;
const init = (time: number) => {
prevTime = time;
requestAnimationFrame(tick);
};
const tick = (time: number) => {
let elapsed = time - prevTime;
prevTime = time;
moveStars(elapsed * speedFactor);
clear();
const cx = w / 2;
const cy = h / 2;
const count = stars.length;
for (var i = 0; i < count; i++) {
const star = stars[i];
const x = cx + star.x / (star.z * 0.001);
const y = cy + star.y / (star.z * 0.001);
if (x < 0 || x >= w || y < 0 || y >= h) {
continue;
}
const d = star.z / 1000.0;
const b = 1 - d * d;
putPixel(x, y, b);
}
requestAnimationFrame(tick);
};
requestAnimationFrame(init);
// add window resize listener:
window.addEventListener('resize', function () {
w = window.innerWidth;
h = window.innerHeight;
setCanvasExtents();
});
} else {
console.error('Could not get 2d context from canvas element');
}
return () => {
window.onresize = null;
};
}, [starColor, backgroundColor, speedFactor, starCount]);
return (
<canvas
id='starfield'
ref={canvasRef}
style={{
padding: 0,
margin: 0,
position: 'fixed',
top: 0,
right: 0,
bottom: 0,
left: 0,
zIndex: 10,
opacity: 1,
pointerEvents: 'none',
mixBlendMode: 'screen',
}}
></canvas>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment