黑膠唱片視覺化
旋轉的黑膠唱片以同心圓 SVG 呈現溝槽紋路,中央紅色標籤配唱針臂,右側 24 根暖色調音頻棒隨 Math.sin 跳動,整體呈現復古氛圍。
音頻黑膠復古旋轉
提示詞(可直接修改內容)
import {
AbsoluteFill,
interpolate,
useCurrentFrame,
useVideoConfig,
} from "remotion";
import React from "react";
const CX = 680;
const CY = 540;
const RECORD-R = 320;
const LABEL-R = 110;
const BAR-COUNT = 24;
export const AudioVinylRecord: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const intro = interpolate(frame, [0, 30], [0, 1], { extrapolateRight: "clamp" });
const rotation = frame * 1.2; // degrees per frame
return (
<AbsoluteFill
style={{
background: "linear-gradient(135deg, #1a0a00 0%, #0d0010 100%)",
fontFamily: "sans-serif",
overflow: "hidden",
}}
>
<svg
width={1920}
height={1080}
style={{ position: "absolute", top: 0, left: 0, opacity: intro }}
>
<defs>
{/* Vinyl groove gradient */}
<radialGradient id="vinylGrad" cx="50%" cy="50%" r="50%">
<stop offset="0%" stopColor="#1a1a1a" />
<stop offset="40%" stopColor="#111111" />
<stop offset="70%" stopColor="#0d0d0d" />
<stop offset="100%" stopColor="#080808" />
</radialGradient>
{/* Label gradient */}
<radialGradient id="labelGrad" cx="50%" cy="50%" r="50%">
<stop offset="0%" stopColor="#cc3300" />
<stop offset="60%" stopColor="#991100" />
<stop offset="100%" stopColor="#660800" />
</radialGradient>
<filter id="vinylShadow">
<feDropShadow dx="8" dy="12" stdDeviation="20" floodOpacity="0.6" />
</filter>
<filter id="barGlow">
<feGaussianBlur stdDeviation="3" result="blur" />
<feMerge>
<feMergeNode in="blur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
{/* Record body */}
<g transform={`rotate(${rotation}, ${CX}, ${CY})`} filter="url(#vinylShadow)">
<circle cx={CX} cy={CY} r={RECORD-R} fill="url(#vinylGrad)" />
{/* Groove rings */}
{Array.from({ length: 18 }).map((_, i) => {
const r = 130 + i * 10;
return (
<circle
key={i}
cx={CX}
cy={CY}
r={r}
fill="none"
stroke="rgba(255,255,255,0.04)"
strokeWidth={1}
/>
);
})}
{/* Outer ring highlight */}
<circle
cx={CX}
cy={CY}
r={RECORD-R - 8}
fill="none"
stroke="rgba(255,255,255,0.08)"
strokeWidth={4}
/>
{/* Label */}
<circle cx={CX} cy={CY} r={LABEL-R} fill="url(#labelGrad)" />
<circle cx={CX} cy={CY} r={LABEL-R - 10} fill="none" stroke="rgba(255,180,100,0.3)" strokeWidth={1} />
<circle cx={CX} cy={CY} r={LABEL-R - 24} fill="none" stroke="rgba(255,180,100,0.2)" strokeWidth={1} />
{/* Center hole */}
<circle cx={CX} cy={CY} r={10} fill="#000000" />
<circle cx={CX} cy={CY} r={8} fill="#1a1a1a" />
</g>
{/* Tonearm */}
<g opacity={intro}>
<line
x1={CX + 370}
y1={CY - 280}
x2={CX + 250}
y2={CY - 40}
stroke="rgba(200,180,120,0.8)"
strokeWidth={6}
strokeLinecap="round"
/>
<circle cx={CX + 370} cy={CY - 280} r={18} fill="#333" stroke="rgba(200,180,120,0.5)" strokeWidth={2} />
</g>
{/* Audio bars on the right side */}
{Array.from({ length: BAR-COUNT }).map((_, i) => {
const speed = 0.07 + (i % 5) * 0.018;
const phase = i * 0.38;
const sinVal = (Math.sin(frame * speed + phase) + 1) / 2;
const barH = 20 + sinVal * 200;
const BAR-W = 20;
const GAP = 6;
const totalW = BAR-COUNT * (BAR-W + GAP) - GAP;
const startX = 1920 - 80 - totalW;
const x = startX + i * (BAR-W + GAP);
const y = CY + barH / 2;
const hue = 20 + (i / BAR-COUNT) * 40;
const color = `hsl(${hue}, 90%, 60%)`;
return (
<rect
key={i}
x={x}
y={CY - barH / 2}
width={BAR-W}
height={barH}
rx={4}
fill={color}
opacity={0.85 * intro}
filter="url(#barGlow)"
/>
);
})}
</svg>
{/* Text panel */}
<div
style={{
position: "absolute",
top: "50%",
right: 80,
transform: "translateY(-50%)",
textAlign: "right",
opacity: intro,
width: 460,
marginRight: 20,
}}
>
<div
style={{
fontSize: 52,
fontWeight: 700,
color: "#ffffff",
letterSpacing: "0.04em",
lineHeight: 1.2,
textShadow: "0 0 24px rgba(220,100,40,0.5)",
}}
>
黑膠唱片
</div>
<div
style={{
fontSize: 20,
color: "#cc6622",
marginTop: 12,
letterSpacing: "0.12em",
}}
>
VINYL RECORD VISUALIZER
</div>
<div
style={{
marginTop: 24,
fontSize: 18,
color: "#6b5040",
lineHeight: 1.8,
}}
>
復古音頻視覺化
<br />
旋轉唱片 × 頻譜律動
</div>
</div>
</AbsoluteFill>
);
};登入後查看完整程式碼