Created
February 16, 2024 05:04
-
-
Save gaurangrshah/52d0e147a5b605e7b849f26fed51b95c to your computer and use it in GitHub Desktop.
canvas starfield react
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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