Import source: src/components/StatsSection/StatsSection

Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
arsam
2026-09-18 23:08:07 +03:30
parent cc284f7f78
commit 0c02eacfec
@@ -0,0 +1,134 @@
import { useRef, useMemo } from 'react'
import { Canvas, useFrame } from '@react-three/fiber'
import { motion, useInView } from 'framer-motion'
import * as THREE from 'three'
const stats = [
{ value: '40M+', label: 'Tracks', sub: 'and growing daily' },
{ value: '2.1M', label: 'Artists', sub: 'worldwide' },
{ value: '180+', label: 'Countries', sub: 'streaming now' },
{ value: '99.9%', label: 'Uptime', sub: 'always on' },
]
const SPIKE_COUNT = 48
function GlobeSpikes() {
const groupRef = useRef()
const spikesRef = useRef([])
const dummy = useMemo(() => new THREE.Object3D(), [])
const spikeData = useMemo(() => {
return Array.from({ length: SPIKE_COUNT }, (_, i) => {
const phi = Math.acos(-1 + (2 * i) / SPIKE_COUNT)
const theta = Math.sqrt(SPIKE_COUNT * Math.PI) * phi
const r = 1.7
return {
x: r * Math.sin(phi) * Math.cos(theta),
y: r * Math.cos(phi),
z: r * Math.sin(phi) * Math.sin(theta),
phi, theta,
phase: Math.random() * Math.PI * 2,
speed: 0.5 + Math.random() * 1.0,
}
})
}, [])
useFrame(({ clock }) => {
const t = clock.getElapsedTime()
if (groupRef.current) groupRef.current.rotation.y = t * 0.12
spikeData.forEach((s, i) => {
if (!spikesRef.current[i]) return
const len = 0.05 + Math.abs(Math.sin(t * s.speed + s.phase)) * 0.55
const dir = new THREE.Vector3(s.x, s.y, s.z).normalize()
dummy.position.set(s.x, s.y, s.z)
dummy.lookAt(s.x + dir.x, s.y + dir.y, s.z + dir.z)
dummy.scale.set(0.02, len, 0.02)
dummy.updateMatrix()
spikesRef.current[i].setMatrixAt(0, dummy.matrix)
spikesRef.current[i].instanceMatrix.needsUpdate = true
})
})
return (
<group ref={groupRef}>
{/* Globe wireframe */}
<mesh>
<sphereGeometry args={[1.7, 24, 24]} />
<meshBasicMaterial color="#c9a84c" transparent opacity={0.04} wireframe />
</mesh>
<mesh>
<sphereGeometry args={[1.65, 32, 32]} />
<meshBasicMaterial color="#c9a84c" transparent opacity={0.02} />
</mesh>
{/* Spikes as instanced meshes */}
{spikeData.map((s, i) => (
<instancedMesh key={i} ref={el => spikesRef.current[i] = el} args={[null, null, 1]}>
<cylinderGeometry args={[1, 0.1, 1, 4]} />
<meshStandardMaterial color="#c9a84c" emissive="#c9a84c" emissiveIntensity={0.8} toneMapped={false} />
</instancedMesh>
))}
</group>
)
}
export default function StatsSection() {
const ref = useRef()
const inView = useInView(ref, { once: true, margin: '-100px' })
return (
<section ref={ref} className="py-28 px-6 relative overflow-hidden" style={{ background: '#050505' }}>
<div className="section-divider" />
<div className="max-w-7xl mx-auto">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-center mt-14">
{/* Stats */}
<div>
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={inView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6 }}
className="mb-12"
>
<p className="text-xs tracking-[0.25em] uppercase gold-text mb-3">By The Numbers</p>
<h2 className="font-display font-bold text-4xl md:text-5xl text-white">
The Scale of <span className="gold-text">Sound</span>
</h2>
</motion.div>
<div className="grid grid-cols-2 gap-6">
{stats.map((stat, i) => (
<motion.div
key={stat.label}
initial={{ opacity: 0, y: 30 }}
animate={inView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.5, delay: i * 0.1 }}
className="relative p-6 rounded-2xl group"
style={{ background: '#101010', border: '1px solid #1e1e1e' }}
>
<div className="absolute inset-0 rounded-2xl opacity-0 group-hover:opacity-100 transition-opacity duration-300"
style={{ boxShadow: 'inset 0 0 0 1px rgba(201,168,76,0.2)' }} />
<p className="font-display font-bold text-3xl md:text-4xl gold-text mb-1">{stat.value}</p>
<p className="text-white font-medium text-sm">{stat.label}</p>
<p className="text-gray-700 text-xs mt-0.5">{stat.sub}</p>
</motion.div>
))}
</div>
</div>
{/* Globe */}
<div style={{ height: '420px' }}>
<Canvas camera={{ position: [0, 0, 6], fov: 50 }} gl={{ alpha: true, antialias: true }} style={{ background: 'transparent' }}>
<ambientLight intensity={0.05} />
<pointLight position={[0, 5, 3]} intensity={5} color="#c9a84c" />
<pointLight position={[-4, -3, 2]} intensity={2} color="#e8c547" />
<pointLight position={[4, 3, -2]} intensity={1.5} color="#8a6f2e" />
<GlobeSpikes />
</Canvas>
</div>
</div>
</div>
</section>
)
}