Files
TSSBOT-web/Tree/FallingLeaves.tsx
T
2026-05-14 21:11:55 +01:00

106 lines
3.0 KiB
TypeScript

import React, { useEffect, useRef } from "react";
import { TRUNK_TOP_CSS } from "./Tree";
// this is a comment
interface Leaf {
x: number;
y: number;
size: number;
speed: number;
drift: number;
rotation: number;
rotationSpeed: number;
opacity: number;
sway: number;
swaySpeed: number;
phase: number;
}
export default function FallingLeaves({ treeRef }: { treeRef: React.RefObject<HTMLCanvasElement | null> }) {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const canvas = canvasRef.current!;
const ctx = canvas.getContext("2d")!;
let animId: number;
const leaves: Leaf[] = [];
const MAX_LEAVES = 50;
function resize() {
const parent = canvas.parentElement;
const rect = parent?.getBoundingClientRect();
canvas.width = Math.max(1, Math.round(rect?.width || window.innerWidth));
canvas.height = Math.max(1, Math.round(rect?.height || window.innerHeight));
}
resize();
window.addEventListener("resize", resize);
function spawnLeaf(): Leaf {
const treeRect = treeRef.current?.getBoundingClientRect();
const canvasRect = canvas.getBoundingClientRect();
const spawnY = treeRect ? treeRect.top - canvasRect.top + TRUNK_TOP_CSS : canvas.height * 0.4;
const spawnX = treeRect
? treeRect.left - canvasRect.left + Math.random() * treeRect.width * 0.8 + treeRect.width * 0.1
: Math.random() * canvas.width * 0.6 + canvas.width * 0.2;
return {
x: spawnX,
y: spawnY,
size: Math.random() * 4 + 2,
speed: Math.random() * 0.3 + 0.15,
drift: Math.random() * 0.5 - 0.25,
rotation: Math.random() * Math.PI * 2,
rotationSpeed: (Math.random() - 0.5) * 0.02,
opacity: Math.random() * 0.25 + 0.08,
sway: Math.random() * 30 + 15,
swaySpeed: Math.random() * 0.008 + 0.003,
phase: Math.random() * Math.PI * 2,
};
}
let spawnTimer = 0;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
spawnTimer++;
if (spawnTimer > 30 && leaves.length < MAX_LEAVES) {
leaves.push(spawnLeaf());
spawnTimer = 0;
}
for (let i = leaves.length - 1; i >= 0; i--) {
const l = leaves[i];
l.y += l.speed;
l.x += l.drift + Math.sin(l.phase) * l.swaySpeed * l.sway * 0.05;
l.phase += l.swaySpeed;
l.rotation += l.rotationSpeed;
ctx.save();
ctx.translate(l.x, l.y);
ctx.rotate(l.rotation);
ctx.globalAlpha = l.opacity;
ctx.fillStyle = "#fdb068";
const s = Math.round(l.size);
ctx.fillRect(-s / 2, -s / 2, s, s);
ctx.restore();
if (l.y > canvas.height + 10) {
leaves.splice(i, 1);
}
}
animId = requestAnimationFrame(draw);
}
draw();
return () => {
cancelAnimationFrame(animId);
window.removeEventListener("resize", resize);
};
}, []);
return <canvas ref={canvasRef} className="falling-leaves" />;
}