Components
Loading preview...
import React, { useEffect, useRef } from 'react';
const ColorBallsPage: React.FC = () => {
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const animationFrameId = useRef<number>();
// Ball type
type Ball = {
x: number;
y: number;
radius: number;
color: string;
vx: number;
vy: number;
};
const ballsCount = 20;
const balls = useRef<Ball[]>([]);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
// Resize canvas to fill window
const resize = () => {
if (canvas) {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
};
resize();
window.addEventListener('resize', resize);
const colors = [
'#FF6B6B',
'#FFD93D',
'#6BCB77',
'#4D96FF',
'#FF8C42',
'#9D4EDD',
'#00B8A9',
'#F6416C',
'#FF9B54',
'#44AF69',
];
// Initialize balls
balls.current = [];
for (let i = 0; i < ballsCount; i++) {
const radius = Math.random() * 20 + 15;
const x = Math.random() * (canvas.width - radius * 2) + radius;
const y = Math.random() * (canvas.height - radius * 2) + radius;
const color = colors[i % colors.length];
const vx = (Math.random() - 0.5) * 1.5;
const vy = (Math.random() - 0.5) * 1.5;
balls.current.push({ x, y, radius, color, vx, vy });
}
// Animation function
const animate = () => {
if (!ctx) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
balls.current.forEach((ball) => {
// Move ball
ball.x += ball.vx;
ball.y += ball.vy;
// Bounce on edges
if (ball.x + ball.radius > canvas.width) {
ball.x = canvas.width - ball.radius;
ball.vx *= -1;
}
if (ball.x - ball.radius < 0) {
ball.x = ball.radius;
ball.vx *= -1;
}
if (ball.y + ball.radius > canvas.height) {
ball.y = canvas.height - ball.radius;
ball.vy *= -1;
}
if (ball.y - ball.radius < 0) {
ball.y = ball.radius;
ball.vy *= -1;
}
// Draw ball
const gradient = ctx.createRadialGradient(
ball.x,
ball.y,
ball.radius * 0.3,
ball.x,
ball.y,
ball.radius
);
gradient.addColorStop(0, ball.color);
gradient.addColorStop(1, 'transparent');
ctx.fillStyle = gradient;
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.fill();
});
animationFrameId.current = requestAnimationFrame(animate);
};
animate();
return () => {
window.removeEventListener('resize', resize);
if (animationFrameId.current) cancelAnimationFrame(animationFrameId.current);
};
}, []);
return (
<>
<canvas
ref={canvasRef}
style={{
position: 'fixed',
top: 0,
left: 0,
width: '100vw',
height: '100vh',
zIndex: -1,
background:
'linear-gradient(135deg, #4b6cb7 0%, #182848 100%)',
display: 'block',
}}
/>
<div
style={{
minHeight: '100vh',
color: '#fff',
fontFamily:
"'Segoe UI', Tahoma, Geneva, Verdana, sans-serif",
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
padding: '2rem',
textAlign: 'center',
userSelect: 'none',
}}
>
<h1
style={{
fontSize: '3rem',
letterSpacing: '2px',
textShadow: '2px 2px 6px rgba(0,0,0,0.4)',
marginBottom: '0.5rem',
}}
>
My Cool Website
</h1>
<p
style={{
fontSize: '1.25rem',
color: '#cbd5e1',
marginBottom: '2rem',
}}
>
Welcome to a clean and modern front page
</p>
<button
onClick={() => alert('You clicked the button!')}
style={{
padding: '0.75rem 2rem',
fontSize: '1.2rem',
color: '#182848',
background: '#f6f6f6',
borderRadius: '30px',
border: 'none',
cursor: 'pointer',
boxShadow: '0 5px 15px rgba(246,246,246,0.6)',
transition: 'background 0.3s ease, color 0.3s ease',
userSelect: 'none',
}}
onMouseEnter={e => {
(e.currentTarget as HTMLButtonElement).style.background = '#d1d5db';
(e.currentTarget as HTMLButtonElement).style.color = '#0f172a';
}}
onMouseLeave={e => {
(e.currentTarget as HTMLButtonElement).style.background = '#f6f6f6';
(e.currentTarget as HTMLButtonElement).style.color = '#182848';
}}
>
Get Started
</button>
<footer
style={{
marginTop: 'auto',
fontSize: '0.9rem',
color: '#94a3b8',
marginTop: '3rem',
userSelect: 'text',
}}
>
© 2025 My Cool Website. All rights reserved
</footer>
</div>
</>
);
};
export default ColorBallsPage;