【前端】弹球特效(重力模拟)

时间:2024-07-18 07:07:54
import React, { useRef, useEffect } from 'react'; const BouncingBallSimulation = () => { const canvasRef = useRef(null); useEffect(() => { const canvas = canvasRef.current; const ctx = canvas.getContext('2d'); let animationFrameId; // Set canvas size canvas.width = 900; canvas.height = 1600; // Circle properties const centerX = canvas.width / 2; const centerY = canvas.height / 2; const radius = Math.min(centerX, centerY) * 0.7; // Ball properties const ballRadius = 15; let x = centerX; let y = centerY - radius + ballRadius; let dx = 2; let dy = 0; // Gravity and friction const gravity = 0.2; const friction = 0.99; // Matrix rain properties const fontSize = 14; const columns = canvas.width / fontSize; const drops = []; for (let i = 0; i < columns; i++) { drops[i] = 1; } const drawMatrixRain = () => { ctx.fillStyle = 'rgba(0, 0, 0, 0.05)'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#0F0'; ctx.font = `${fontSize}px monospace`; for (let i = 0; i < drops.length; i++) { const text = String.fromCharCode(0x30A0 + Math.random() * 96); ctx.fillText(text, i * fontSize, drops[i] * fontSize); if (drops[i] * fontSize > canvas.height && Math.random() > 0.975) { drops[i] = 0; } drops[i]++; } }; const draw = () => { ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw matrix rain drawMatrixRain(); // Draw circle ctx.beginPath(); ctx.arc(centerX, centerY, radius, 0, Math.PI * 2); ctx.strokeStyle = 'rgba(255, 255, 255, 0.5)'; ctx.stroke(); // Draw ball ctx.beginPath(); ctx.arc(x, y, ballRadius, 0, Math.PI * 2); ctx.fillStyle = 'white'; ctx.fill(); // Apply gravity dy += gravity; // Ball movement and collision detection const nextX = x + dx; const nextY = y + dy; const distanceFromCenter = Math.sqrt((nextX - centerX) ** 2 + (nextY - centerY) ** 2); if (distanceFromCenter + ballRadius > radius) { // Calculate the normal vector const nx = (nextX - centerX) / distanceFromCenter; const ny = (nextY - centerY) / distanceFromCenter; // Calculate the dot product const dotProduct = dx * nx + dy * ny; // Update velocity dx = (dx - 2 * dotProduct * nx) * friction; dy = (dy - 2 * dotProduct * ny) * friction; } else { x = nextX; y = nextY; } animationFrameId = requestAnimationFrame(draw); }; draw(); return () => { cancelAnimationFrame(animationFrameId); }; }, []); return ( <div className="flex items-center justify-center w-full h-full bg-black"> <canvas ref={canvasRef} className="border border-gray-300" /> </div> ); }; export default BouncingBallSimulation;