Files
nearle_web_nextjs/src/components/home/voice/VoiceWaves.tsx
2026-07-24 15:28:46 +05:30

70 lines
2.7 KiB
TypeScript

"use client";
import { motion, useReducedMotion } from "framer-motion";
// A 2400-wide path holding six 400px periods. Sliding it exactly -1200
// (three periods) loops seamlessly, so the drift never visibly restarts.
const WAVE =
"M0 150 Q100 80 200 150 T400 150 T600 150 T800 150 T1000 150 T1200 150 T1400 150 T1600 150 T1800 150 T2000 150 T2200 150 T2400 150";
// Opacities are tuned for a pure white canvas. On the old lavender background
// these read as texture; on white anything stronger reads as clutter and pulls
// attention off the phone.
const LAYERS = [
{ y: 0, scaleY: 1, opacity: 0.1, duration: 12, width: 2 },
{ y: 26, scaleY: 0.62, opacity: 0.07, duration: 16, width: 1.5 },
{ y: -22, scaleY: 1.35, opacity: 0.055, duration: 21, width: 1.5 },
];
// Elegant voice waves behind the phone. Purely decorative, low opacity, and
// animated on transform only — it should reinforce "this is voice" without
// ever pulling attention off the screen.
export function VoiceWaves({ className = "" }: { className?: string }) {
const reduced = useReducedMotion();
return (
<div
aria-hidden="true"
className={`pointer-events-none absolute inset-0 overflow-hidden ${className}`}
style={{
maskImage: "radial-gradient(60% 55% at 50% 50%, #000 35%, transparent 100%)",
WebkitMaskImage: "radial-gradient(60% 55% at 50% 50%, #000 35%, transparent 100%)",
}}
>
{LAYERS.map((layer, i) => (
<motion.svg
key={i}
viewBox="0 0 2400 300"
preserveAspectRatio="none"
className="absolute left-1/2 top-1/2 h-[52%] w-[200%]"
style={{
x: "-50%",
y: `calc(-50% + ${layer.y}px)`,
opacity: layer.opacity,
}}
animate={reduced ? undefined : { translateX: ["0%", "-25%"] }}
transition={{ duration: layer.duration, repeat: Infinity, ease: "linear" }}
>
<defs>
<linearGradient id={`voice-wave-${i}`} x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stopColor="#7C3AED" stopOpacity="0" />
<stop offset="35%" stopColor="#A855F7" stopOpacity="1" />
<stop offset="65%" stopColor="#C084FC" stopOpacity="1" />
<stop offset="100%" stopColor="#7C3AED" stopOpacity="0" />
</linearGradient>
</defs>
<path
d={WAVE}
fill="none"
stroke={`url(#voice-wave-${i})`}
strokeWidth={layer.width}
strokeLinecap="round"
vectorEffect="non-scaling-stroke"
transform={`translate(0 ${150 - 150 * layer.scaleY}) scale(1 ${layer.scaleY})`}
/>
</motion.svg>
))}
</div>
);
}