社群連結片尾
「追蹤我們」標題從上方淡入,YouTube、Instagram、Twitter/X、GitHub 四個平台圖示以交錯彈簧動畫依序滑入,最後整體淡出收場。
片尾社群連結
提示詞(可直接修改內容)
import React from "react";
import {
AbsoluteFill,
interpolate,
spring,
useCurrentFrame,
useVideoConfig,
} from "remotion";
type Platform = {
name: string;
handle: string;
icon: string;
background: string;
shape: "roundedRect" | "circle";
};
const platforms: Platform[] = [
{
name: "YouTube",
handle: "@mychannel",
icon: "▶",
background: "#ff0000",
shape: "roundedRect",
},
{
name: "Instagram",
handle: "@myinsta",
icon: "◈",
background: "linear-gradient(135deg, #833ab4 0%, #fd1d1d 50%, #fcb045 100%)",
shape: "circle",
},
{
name: "Twitter / X",
handle: "@mytwitter",
icon: "✕",
background: "#000000",
shape: "circle",
},
{
name: "GitHub",
handle: "@mygithub",
icon: "GH",
background: "#24292e",
shape: "circle",
},
];
const STAGGER-FRAMES = 20;
const PLATFORM-START = 30; // 平台進場開始 frame
const FADEOUT-START = 150; // 全體 fade out 開始 frame
export const SocialLinksOutro: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
// 標題:frame 0-30 從上方淡入
const titleOpacity = interpolate(frame, [0, 30], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
const titleY = interpolate(frame, [0, 30], [-30, 0], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
// 全體 fade out:frame 150-180
const globalOpacity = interpolate(frame, [FADEOUT-START, FADEOUT-START + 30], [1, 0], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
return (
<AbsoluteFill
style={{
background: "linear-gradient(180deg, #0f172a 0%, #1e1b4b 100%)",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
opacity: globalOpacity,
}}
>
{/* 標題 */}
<div
style={{
transform: `translateY(${titleY}px)`,
opacity: titleOpacity,
fontSize: 48,
fontWeight: 800,
color: "#ffffff",
fontFamily: "sans-serif",
letterSpacing: "4px",
marginBottom: 80,
}}
>
追蹤我們
</div>
{/* 平台列表 */}
<div
style={{
display: "flex",
flexDirection: "row",
alignItems: "flex-start",
gap: 64,
}}
>
{platforms.map((platform, i) => {
const startFrame = PLATFORM-START + i * STAGGER-FRAMES;
const itemSpring = spring({
frame: frame - startFrame,
fps,
config: { damping: 20, stiffness: 140 },
durationInFrames: 20,
});
const itemY = interpolate(itemSpring, [0, 1], [40, 0]);
const itemOpacity = interpolate(itemSpring, [0, 0.4], [0, 1], {
extrapolateRight: "clamp",
});
const isRoundedRect = platform.shape === "roundedRect";
return (
<div
key={platform.name}
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: 16,
transform: `translateY(${itemY}px)`,
opacity: itemOpacity,
}}
>
{/* 圖示 */}
<div
style={{
width: 80,
height: 80,
borderRadius: isRoundedRect ? 16 : "50%",
background: platform.background,
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: platform.icon === "GH" ? 20 : 32,
color: "#ffffff",
fontFamily: "sans-serif",
fontWeight: 700,
flexShrink: 0,
// 為 Instagram 加外框增強辨識度
boxShadow:
platform.name === "Instagram"
? "0 0 0 2px rgba(255,255,255,0.15)"
: "none",
}}
>
{platform.icon}
</div>
{/* 平台名稱 */}
<div
style={{
fontSize: 18,
fontWeight: 700,
color: "#ffffff",
fontFamily: "sans-serif",
letterSpacing: "0.5px",
}}
>
{platform.name}
</div>
{/* 帳號名稱 */}
<div
style={{
fontSize: 15,
color: "#94a3b8",
fontFamily: "sans-serif",
}}
>
{platform.handle}
</div>
</div>
);
})}
</div>
</AbsoluteFill>
);
};登入後查看完整程式碼