Reddit 動態牆
Reddit 深色模式動態牆,4 篇貼文由下往上依序滑入,左側橙色投票欄位數字動態計數,右側顯示 Subreddit、發文者、標題與操作列,帶有交錯延遲的彈性動畫。
社群簡約橫式
提示詞(可直接修改內容)
import {
AbsoluteFill,
interpolate,
spring,
useCurrentFrame,
useVideoConfig,
} from "remotion";
import React from "react";
const POSTS = [
{
sub: "r/webdev",
user: "u/remotion-fan",
time: "3小時前",
votes: 4821,
comments: 342,
title: "用 React 寫影片?Remotion 讓這件事成真了,附完整教學",
},
{
sub: "r/programming",
user: "u/typescript-lover",
time: "5小時前",
votes: 2341,
comments: 187,
title: "TypeScript 5.0 新功能讓我的開發效率提升了 30%,心得分享",
},
{
sub: "r/opensource",
user: "u/oss-tw",
time: "8小時前",
votes: 1876,
comments: 124,
title: "分享一個用 React 做的開源動畫工具,GitHub 已破 20k ⭐",
},
{
sub: "r/cscareerquestions",
user: "u/junior-dev-tw",
time: "12小時前",
votes: 987,
comments: 89,
title: "台灣前端工程師薪資調查 2025 — 整理了 500 份問卷的結果",
},
];
const SUB-COLORS = ["#ff4500", "#ff6534", "#ff6534", "#ff4500"];
const CARD-WIDTH = 900;
const CARD-HEIGHT = 150;
const CARD-GAP = 18;
const TOTAL-HEIGHT =
POSTS.length * CARD-HEIGHT + (POSTS.length - 1) * CARD-GAP;
const CARDS-TOP = (1080 - TOTAL-HEIGHT) / 2;
function formatVotes(n: number): string {
if (n >= 1000) return (n / 1000).toFixed(1).replace(/\.0$/, "") + "k";
return String(n);
}
export const RedditFeed: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const cardProgressList = POSTS.map((_, i) => {
const startFrame = i * 18 + 5;
return spring({
frame: frame - startFrame,
fps,
config: { damping: 22, stiffness: 150 },
durationInFrames: 20,
});
});
const voteProgressList = POSTS.map((_, i) => {
const start = i * 18 + 5 + 20;
return interpolate(frame, [start, start + 40], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
});
return (
<AbsoluteFill
style={{
background: "#0f0f0f",
fontFamily: "sans-serif",
alignItems: "center",
}}
>
<div
style={{
position: "absolute",
top: 36,
right: 80,
display: "flex",
alignItems: "center",
gap: 10,
}}
>
<div
style={{
width: 36,
height: 36,
borderRadius: "50%",
background: "#ff4500",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: 20,
}}
>
👽
</div>
<span
style={{ fontSize: 22, fontWeight: 900, color: "rgba(255,255,255,0.15)" }}
>
reddit
</span>
</div>
{POSTS.map((post, i) => {
const progress = cardProgressList[i];
const voteProgress = voteProgressList[i];
const translateY = interpolate(progress, [0, 1], [50, 0]);
const opacity = interpolate(progress, [0, 0.35], [0, 1], {
extrapolateRight: "clamp",
});
const currentVotes = Math.round(voteProgress * post.votes);
const cardTop = CARDS-TOP + i * (CARD-HEIGHT + CARD-GAP);
return (
<div
key={i}
style={{
position: "absolute",
top: cardTop,
width: CARD-WIDTH,
background: "#0d1117",
border: "1px solid #343536",
borderRadius: 8,
display: "flex",
flexDirection: "row",
transform: `translateY(${translateY}px)`,
opacity,
boxSizing: "border-box",
overflow: "hidden",
boxShadow: "0 4px 24px rgba(0,0,0,0.5)",
}}
>
<div
style={{
width: 54,
background: "#161617",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
gap: 4,
padding: "12px 0",
flexShrink: 0,
}}
>
<span style={{ fontSize: 18, color: "#ff4500", lineHeight: 1 }}>
▲
</span>
<span
style={{
fontSize: 13,
fontWeight: 700,
color: "#ff4500",
lineHeight: 1.2,
textAlign: "center",
minWidth: 40,
}}
>
{formatVotes(currentVotes)}
</span>
<span style={{ fontSize: 18, color: "#818384", lineHeight: 1 }}>
▽
</span>
</div>
<div
style={{
flex: 1,
padding: "14px 18px",
display: "flex",
flexDirection: "column",
justifyContent: "space-between",
minWidth: 0,
}}
>
<div
style={{
display: "flex",
alignItems: "center",
gap: 8,
marginBottom: 8,
flexWrap: "nowrap",
}}
>
<div
style={{
width: 20,
height: 20,
borderRadius: "50%",
background: SUB-COLORS[i],
flexShrink: 0,
}}
/>
<span
style={{
fontSize: 14,
fontWeight: 700,
color: "#d7dadc",
whiteSpace: "nowrap",
}}
>
{post.sub}
</span>
<span style={{ fontSize: 13, color: "#818384", whiteSpace: "nowrap" }}>
· {post.user} · {post.time}
</span>
</div>
<div
style={{
fontSize: 18,
fontWeight: 700,
color: "#d7dadc",
lineHeight: 1.4,
marginBottom: 10,
overflow: "hidden",
display: "-webkit-box",
WebkitLineClamp: 2,
WebkitBoxOrient: "vertical",
}}
>
{post.title}
</div>
<div
style={{
display: "flex",
gap: 6,
alignItems: "center",
}}
>
{[
{ icon: "💬", label: `${post.comments} 則留言` },
{ icon: "↗", label: "分享" },
{ icon: "🔖", label: "儲存" },
].map(({ icon, label }) => (
<div
key={label}
style={{
display: "flex",
alignItems: "center",
gap: 5,
padding: "5px 10px",
borderRadius: 2,
fontSize: 13,
fontWeight: 700,
color: "#818384",
}}
>
<span style={{ fontSize: 14 }}>{icon}</span>
<span>{label}</span>
</div>
))}
</div>
</div>
</div>
);
})}
</AbsoluteFill>
);
};登入後查看完整程式碼