Remotion LabRemotion Lab
返回模板庫

熱門話題牆

仿 Twitter 熱門趨勢面板,8 個話題排成 2×4 網格,依序從左滑入,排名以金銀銅色標示,熱門話題帶有 🔥 徽章與微光效果,類別徽章彩色標示。

社群簡約橫式
提示詞(可直接修改內容)
import {
  AbsoluteFill,
  interpolate,
  spring,
  useCurrentFrame,
  useVideoConfig,
} from "remotion";
import React from "react";

const TRENDS = [
  { rank: 1,  category: "科技",   tag: "#Remotion",       count: "4.2萬", hot: true },
  { rank: 2,  category: "程式",   tag: "#TypeScript",     count: "3.8萬", hot: true },
  { rank: 3,  category: "設計",   tag: "#UIDesign",       count: "2.9萬", hot: false },
  { rank: 4,  category: "台灣",   tag: "#台北科技周",      count: "2.4萬", hot: true },
  { rank: 5,  category: "娛樂",   tag: "#動漫推薦",        count: "2.1萬", hot: false },
  { rank: 6,  category: "職涯",   tag: "#工程師求職",      count: "1.8萬", hot: false },
  { rank: 7,  category: "科技",   tag: "#AI工具",          count: "1.6萬", hot: true },
  { rank: 8,  category: "新聞",   tag: "#科技新聞",        count: "1.3萬", hot: false },
];

const RANK-COLORS = [
  "#fbbf24",
  "#9ca3af",
  "#cd7c2f",
  "#6b7280",
  "#6b7280",
  "#6b7280",
  "#6b7280",
  "#6b7280",
];

const CATEGORY-COLORS: Record<string, string> = {
  科技: "#3b82f6",
  程式: "#8b5cf6",
  設計: "#ec4899",
  台灣: "#10b981",
  娛樂: "#f59e0b",
  職涯: "#06b6d4",
  新聞: "#ef4444",
};

const COLS = 2;
const ROW-H = 88;
const ROW-GAP = 12;
const COL-GAP = 40;
const COL-W = 820;
const GRID-W = COLS * COL-W + COL-GAP;
const GRID-LEFT = (1920 - GRID-W) / 2;
const GRID-TOP = 160;

const ITEM-POSITIONS = TRENDS.map((_, i) => ({
  col: i % COLS,
  row: Math.floor(i / COLS),
}));

export const TrendingHashtags: React.FC = () => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();

  const titleProgress = spring({
    frame,
    fps,
    config: { damping: 22, stiffness: 140 },
    durationInFrames: 20,
  });
  const titleY = interpolate(titleProgress, [0, 1], [-30, 0]);
  const titleOpacity = interpolate(titleProgress, [0, 0.4], [0, 1], {
    extrapolateRight: "clamp",
  });

  const rowProgressList = TRENDS.map((_, i) => {
    const startFrame = i * 10 + 5;
    return spring({
      frame: frame - startFrame,
      fps,
      config: { damping: 22, stiffness: 160 },
      durationInFrames: 16,
    });
  });

  return (
    <AbsoluteFill
      style={{
        background: "#0a0a0f",
        fontFamily: "sans-serif",
      }}
    >
      <div
        style={{
          position: "absolute",
          inset: 0,
          backgroundImage:
            "linear-gradient(rgba(255,255,255,0.02) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.02) 1px, transparent 1px)",
          backgroundSize: "60px 60px",
          pointerEvents: "none",
        }}
      />

      <div
        style={{
          position: "absolute",
          top: 50,
          left: 0,
          right: 0,
          display: "flex",
          justifyContent: "center",
          alignItems: "center",
          gap: 16,
          transform: `translateY(${titleY}px)`,
          opacity: titleOpacity,
        }}
      >
        <div
          style={{
            fontSize: 16,
            fontWeight: 700,
            color: "#6b7280",
            letterSpacing: 3,
            textTransform: "uppercase",
          }}
        >
          Trending
        </div>
        <div
          style={{
            width: 6,
            height: 6,
            borderRadius: "50%",
            background: "#6b7280",
          }}
        />
        <div
          style={{
            fontSize: 36,
            fontWeight: 900,
            color: "#ffffff",
            letterSpacing: 1,
          }}
        >
          今日熱門話題
        </div>
        <div
          style={{
            background: "linear-gradient(135deg, #3b82f6, #8b5cf6)",
            borderRadius: 20,
            padding: "4px 16px",
            fontSize: 14,
            fontWeight: 700,
            color: "#ffffff",
          }}
        >
          即時更新
        </div>
      </div>

      {TRENDS.map((trend, i) => {
        const progress = rowProgressList[i];
        const { col, row } = ITEM-POSITIONS[i];

        const translateX = interpolate(progress, [0, 1], [-60, 0]);
        const opacity = interpolate(progress, [0, 0.35], [0, 1], {
          extrapolateRight: "clamp",
        });

        const left = GRID-LEFT + col * (COL-W + COL-GAP);
        const top = GRID-TOP + row * (ROW-H + ROW-GAP);
        const catColor = CATEGORY-COLORS[trend.category] ?? "#6b7280";

        const glowOpacity = trend.hot
          ? interpolate(
              frame,
              [0, 15, 30],
              [0.4, 0.8, 0.4],
              { extrapolateLeft: "clamp", extrapolateRight: "clamp" }
            )
          : 0;

        return (
          <div
            key={i}
            style={{
              position: "absolute",
              left,
              top,
              width: COL-W,
              height: ROW-H,
              transform: `translateX(${translateX}px)`,
              opacity,
              display: "flex",
              alignItems: "center",
              gap: 18,
              background: trend.hot
                ? "rgba(251,191,36,0.04)"
                : "rgba(255,255,255,0.025)",
              borderRadius: 12,
              border: `1px solid ${trend.hot ? "rgba(251,191,36,0.15)" : "rgba(255,255,255,0.06)"}`,
              padding: "0 22px",
              boxSizing: "border-box",
              boxShadow: trend.hot
                ? `0 0 30px rgba(251,191,36,${glowOpacity * 0.3})`
                : "none",
            }}
          >
            <div
              style={{
                fontSize: 32,
                fontWeight: 900,
                color: RANK-COLORS[i],
                minWidth: 40,
                textAlign: "center",
                lineHeight: 1,
                textShadow:
                  i < 3 ? `0 0 20px ${RANK-COLORS[i]}88` : "none",
              }}
            >
              {trend.rank}
            </div>

            <div
              style={{
                background: `${catColor}22`,
                border: `1px solid ${catColor}55`,
                borderRadius: 20,
                padding: "4px 12px",
                fontSize: 12,
                fontWeight: 700,
                color: catColor,
                whiteSpace: "nowrap",
                flexShrink: 0,
              }}
            >
              {trend.category}
            </div>

            <div
              style={{
                flex: 1,
                fontSize: 22,
                fontWeight: 800,
                color: "#ffffff",
                minWidth: 0,
                overflow: "hidden",
                textOverflow: "ellipsis",
                whiteSpace: "nowrap",
              }}
            >
              {trend.tag}
            </div>

            <div
              style={{
                fontSize: 14,
                color: "#71717a",
                whiteSpace: "nowrap",
                flexShrink: 0,
              }}
            >
              {trend.count} 則貼文
            </div>

            <div
              style={{
                flexShrink: 0,
                fontSize: 18,
                lineHeight: 1,
              }}
            >
              {trend.hot ? "🔥" : <span style={{ color: "#22c55e" }}>↑</span>}
            </div>
          </div>
        );
      })}
    </AbsoluteFill>
  );
};

登入後查看完整程式碼