Added background
Added About
This commit is contained in:
237
src/Components/BackgroundCanvas/BackgroundCanvas.jsx
Normal file
237
src/Components/BackgroundCanvas/BackgroundCanvas.jsx
Normal file
@ -0,0 +1,237 @@
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import './BackgroundCanvas.css';
|
||||
|
||||
const BackgroundCanvas = ({ theme = 'light' }) => {
|
||||
const canvasRef = useRef(null);
|
||||
const animationRef = useRef(null);
|
||||
const jointsRef = useRef([]);
|
||||
const cameraRef = useRef(null);
|
||||
const keysRef = useRef(new Array(127).fill(0));
|
||||
const worldRef = useRef({ width: 0, height: 0 });
|
||||
const themeRef = useRef(theme);
|
||||
|
||||
class Camera {
|
||||
constructor(position, zoom) {
|
||||
this.position = position;
|
||||
this.speed = 3;
|
||||
this.acceleration = { x: 0, y: 0, z: 0 };
|
||||
this.zoom = zoom;
|
||||
}
|
||||
}
|
||||
|
||||
class Joint {
|
||||
constructor(position, vector) {
|
||||
this.position = position;
|
||||
this.vector = vector;
|
||||
this.speed = 0.5;
|
||||
this.w = 2;
|
||||
this.h = 2;
|
||||
this.bone_length = 150;
|
||||
}
|
||||
}
|
||||
|
||||
const generateJoints = (numb, worldWidth, worldHeight) => {
|
||||
const arr = new Array(numb).fill(0);
|
||||
arr.forEach((v, i) => {
|
||||
arr[i] = new Joint(
|
||||
{
|
||||
x: Math.random() * worldWidth,
|
||||
y: Math.random() * worldHeight,
|
||||
},
|
||||
{
|
||||
x: Math.random() * 2 - 1,
|
||||
y: Math.random() * 2 - 1,
|
||||
}
|
||||
);
|
||||
});
|
||||
return arr;
|
||||
};
|
||||
|
||||
const moveJoints = (joints, world) => {
|
||||
joints.forEach((joint) => {
|
||||
joint.position.x += joint.vector.x * joint.speed;
|
||||
joint.position.y += joint.vector.y * joint.speed;
|
||||
|
||||
if (joint.position.x < 0) joint.position.x = world.width;
|
||||
if (joint.position.x > world.width) joint.position.x = 0;
|
||||
if (joint.position.y < 0) joint.position.y = world.height;
|
||||
if (joint.position.y > world.height) joint.position.y = 0;
|
||||
});
|
||||
};
|
||||
|
||||
const drawJoints = (ctx, joints, camera, view, theme) => {
|
||||
const len = joints.length;
|
||||
|
||||
let lineColor, pointColor, backgroundColor;
|
||||
if (theme === 'dark') {
|
||||
backgroundColor = '#000000';
|
||||
lineColor = '#FFFFFF';
|
||||
pointColor = '#F5EEE6';
|
||||
} else {
|
||||
backgroundColor = '#FFFFFF';
|
||||
lineColor = '#131313';
|
||||
pointColor = '#041c40';
|
||||
}
|
||||
|
||||
ctx.fillStyle = backgroundColor;
|
||||
ctx.fillRect(0, 0, view.width, view.height);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
for (let j = i + 1; j < len; j += 3) {
|
||||
const length = Math.hypot(
|
||||
joints[j].position.x - joints[i].position.x,
|
||||
joints[j].position.y - joints[i].position.y
|
||||
);
|
||||
|
||||
if (length <= joints[i].bone_length) {
|
||||
ctx.beginPath();
|
||||
ctx.strokeStyle = lineColor;
|
||||
ctx.lineWidth = camera.zoom * (30 / length);
|
||||
ctx.moveTo(
|
||||
view.width / 2 + (joints[i].position.x - camera.position.x) * camera.zoom,
|
||||
view.height / 2 + (joints[i].position.y - camera.position.y) * camera.zoom
|
||||
);
|
||||
ctx.lineTo(
|
||||
view.width / 2 + (joints[j].position.x - camera.position.x) * camera.zoom,
|
||||
view.height / 2 + (joints[j].position.y - camera.position.y) * camera.zoom
|
||||
);
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
}
|
||||
}
|
||||
|
||||
ctx.fillStyle = pointColor;
|
||||
ctx.fillRect(
|
||||
view.width / 2 + ((joints[i].position.x - camera.position.x) - joints[i].w / 2) * camera.zoom,
|
||||
view.height / 2 + ((joints[i].position.y - camera.position.y) - joints[i].w / 2) * camera.zoom,
|
||||
joints[i].w * camera.zoom,
|
||||
joints[i].h * camera.zoom
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const moveCamera = (camera, keys) => {
|
||||
if (keys[37]) camera.acceleration.x -= camera.speed;
|
||||
if (keys[38]) camera.acceleration.y -= camera.speed;
|
||||
if (keys[39]) camera.acceleration.x += camera.speed;
|
||||
if (keys[40]) camera.acceleration.y += camera.speed;
|
||||
if (keys[188]) camera.acceleration.z += 0.003;
|
||||
if (keys[190]) camera.acceleration.z -= 0.003;
|
||||
|
||||
camera.position.x += camera.acceleration.x;
|
||||
camera.position.y += camera.acceleration.y;
|
||||
camera.zoom += camera.acceleration.z;
|
||||
|
||||
camera.acceleration.x *= 0.96;
|
||||
camera.acceleration.y *= 0.96;
|
||||
camera.acceleration.z *= 0.9;
|
||||
};
|
||||
|
||||
const initCanvas = () => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
const view = {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
};
|
||||
|
||||
canvas.width = view.width;
|
||||
canvas.height = view.height;
|
||||
|
||||
worldRef.current = {
|
||||
width: view.width * 2,
|
||||
height: view.height * 2,
|
||||
};
|
||||
|
||||
cameraRef.current = new Camera(
|
||||
{
|
||||
x: worldRef.current.width / 2,
|
||||
y: worldRef.current.height / 2,
|
||||
},
|
||||
1
|
||||
);
|
||||
|
||||
jointsRef.current = generateJoints(300, worldRef.current.width, worldRef.current.height);
|
||||
};
|
||||
|
||||
const animate = () => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
const view = {
|
||||
width: canvas.width,
|
||||
height: canvas.height,
|
||||
};
|
||||
|
||||
moveCamera(cameraRef.current, keysRef.current);
|
||||
moveJoints(jointsRef.current, worldRef.current);
|
||||
|
||||
drawJoints(ctx, jointsRef.current, cameraRef.current, view, themeRef.current);
|
||||
|
||||
animationRef.current = requestAnimationFrame(animate);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
themeRef.current = theme;
|
||||
}, [theme]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e) => {
|
||||
keysRef.current[e.keyCode] = 1;
|
||||
};
|
||||
|
||||
const handleKeyUp = (e) => {
|
||||
keysRef.current[e.keyCode] = 0;
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
window.addEventListener('keyup', handleKeyUp);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
window.removeEventListener('keyup', handleKeyUp);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handleResize = () => {
|
||||
const canvas = canvasRef.current;
|
||||
if (!canvas) return;
|
||||
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
|
||||
worldRef.current = {
|
||||
width: canvas.width * 2,
|
||||
height: canvas.height * 2,
|
||||
};
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
initCanvas();
|
||||
animate();
|
||||
|
||||
return () => {
|
||||
if (animationRef.current) {
|
||||
cancelAnimationFrame(animationRef.current);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
className="background-canvas"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default BackgroundCanvas;
|
||||
Reference in New Issue
Block a user