116 lines
3.3 KiB
TypeScript
116 lines
3.3 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useRef } from 'react';
|
|
import { colors } from '@/lib/colors';
|
|
|
|
function lerp(a: number, b: number, t: number) {
|
|
return (b - a) * t + a;
|
|
}
|
|
|
|
// Convert hex to RGB
|
|
function hexToRgb(hex: string): { r: number; g: number; b: number } {
|
|
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
return result
|
|
? {
|
|
r: parseInt(result[1], 16),
|
|
g: parseInt(result[2], 16),
|
|
b: parseInt(result[3], 16),
|
|
}
|
|
: { r: 0, g: 0, b: 0 };
|
|
}
|
|
|
|
// Interpolate between two hex colors
|
|
function interpolateColor(color1: string, color2: string, t: number): string {
|
|
const rgb1 = hexToRgb(color1);
|
|
const rgb2 = hexToRgb(color2);
|
|
const r = Math.round(lerp(rgb1.r, rgb2.r, t));
|
|
const g = Math.round(lerp(rgb1.g, rgb2.g, t));
|
|
const b = Math.round(lerp(rgb1.b, rgb2.b, t));
|
|
return `rgb(${r}, ${g}, ${b})`;
|
|
}
|
|
|
|
function color(i: number, total: number) {
|
|
const t = i / total;
|
|
// Interpolate between Webklar colors: primary (dark green) -> secondary (medium green) -> tertiary (light green-beige)
|
|
if (t < 0.5) {
|
|
return interpolateColor(colors.primary, colors.secondary, t * 2);
|
|
} else {
|
|
return interpolateColor(colors.secondary, colors.tertiary, (t - 0.5) * 2);
|
|
}
|
|
}
|
|
|
|
function createWheel(i: number, total: number) {
|
|
const distance = i + 3.5; // Slightly increased distance
|
|
const charWidth = 0.85;
|
|
const speed = 1;
|
|
const circum = distance * 2 * Math.PI;
|
|
const numbers = Math.floor(circum / charWidth);
|
|
const time = speed * numbers;
|
|
const t = i / total;
|
|
|
|
return {
|
|
time,
|
|
numbers,
|
|
distance,
|
|
color: color(i, total),
|
|
scale: lerp(1, 0.25, t * t * 0.5), // Medium scale
|
|
};
|
|
}
|
|
|
|
export default function SpinningNumbers() {
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
|
|
useEffect(() => {
|
|
if (!containerRef.current) return;
|
|
|
|
const container = containerRef.current;
|
|
const total = 13;
|
|
const wheels = Array.from({ length: total }, (_, i) => createWheel(i, total));
|
|
|
|
wheels.forEach((wheel) => {
|
|
const { time, numbers, distance, color: wheelColor, scale } = wheel;
|
|
const angleDiff = (Math.PI * 2) / numbers;
|
|
const divs = [];
|
|
|
|
for (let i = 0; i < numbers; i++) {
|
|
divs.push(angleDiff * i);
|
|
}
|
|
|
|
const wheelDiv = document.createElement('div');
|
|
wheelDiv.className = 'wheel';
|
|
wheelDiv.style.color = wheelColor;
|
|
wheelDiv.style.setProperty('--l', `${distance}em`);
|
|
wheelDiv.style.setProperty('--m', `${numbers}`);
|
|
wheelDiv.style.setProperty('--t', `${time}s`);
|
|
wheelDiv.style.setProperty('--r1', Math.random() < 0.5 ? 'reverse' : 'normal');
|
|
wheelDiv.style.setProperty('--s', `${scale}`);
|
|
|
|
divs.forEach((angle, i) => {
|
|
if (Math.sqrt(Math.random()) < scale) {
|
|
const numberDiv = document.createElement('div');
|
|
numberDiv.className = 'number';
|
|
numberDiv.style.setProperty('--a', `${(angle * 180) / Math.PI}deg`);
|
|
numberDiv.style.setProperty('--i', `${i}`);
|
|
numberDiv.style.setProperty('--r', Math.random() < 0.5 ? 'reverse' : 'normal');
|
|
|
|
wheelDiv.appendChild(numberDiv);
|
|
}
|
|
});
|
|
|
|
container.appendChild(wheelDiv);
|
|
});
|
|
|
|
// Cleanup function
|
|
return () => {
|
|
if (container) {
|
|
container.innerHTML = '';
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<div className="spinning-number" ref={containerRef} />
|
|
);
|
|
}
|
|
|