Remotion LabRemotion Lab
返回模板庫

Product Hunt 產品發布卡

仿 Product Hunt 風格的產品發布動態卡片,展示產品 Logo、名稱、標語、標籤、票數動畫倒數與 CTA 按鈕,採用 PH 招牌橘紅配色。

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

const PRODUCT-NAME = "AwesomeTool";
const TAGLINE = "讓工作效率提升 10 倍的 AI 工具";
const DESCRIPTION =
  "這是一款革命性的工具,幫助創作者和開發者更快地完成工作。整合 AI 技術,自動化繁瑣任務,讓你專注於真正重要的事。";
const TAGS = ["#生產力", "#AI", "#設計工具"];
const VOTES-TARGET = 856;
const COMMENTS = 124;
const RANK = "#1 今日產品";
const PH-ORANGE = "#ff6154";

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

  // Card: scale 0.9→1 + fade, frames 0-20
  const cardProgress = spring({
    frame,
    fps,
    config: { damping: 22, stiffness: 140 },
    durationInFrames: 20,
  });
  const cardScale = interpolate(cardProgress, [0, 1], [0.9, 1]);
  const cardOpacity = interpolate(cardProgress, [0, 0.3], [0, 1], {
    extrapolateRight: "clamp",
  });

  // Logo: scale pop 0→1.1→1, frames 10-28
  const logoProgress = spring({
    frame: frame - 10,
    fps,
    config: { damping: 14, stiffness: 200 },
    durationInFrames: 18,
  });
  const logoScale = interpolate(logoProgress, [0, 0.7, 1], [0, 1.15, 1], {
    extrapolateRight: "clamp",
  });

  // Product name + tagline: slide from right, frames 18-35
  const nameProgress = spring({
    frame: frame - 18,
    fps,
    config: { damping: 22, stiffness: 160 },
    durationInFrames: 17,
  });
  const nameX = interpolate(nameProgress, [0, 1], [50, 0]);
  const nameOpacity = interpolate(nameProgress, [0, 0.4], [0, 1], {
    extrapolateRight: "clamp",
  });

  // Tags: pop in stagger, frames 30-55
  const tagScales = TAGS.map((_, i) => {
    const p = spring({
      frame: frame - (30 + i * 8),
      fps,
      config: { damping: 16, stiffness: 220 },
      durationInFrames: 10,
    });
    return interpolate(p, [0, 1], [0, 1], { extrapolateRight: "clamp" });
  });

  // Description: fade in, frames 40-60
  const descOpacity = interpolate(frame, [40, 60], [0, 1], {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
  });

  // Vote count: count up 0→856, frames 55-90
  const votesRaw = interpolate(frame, [55, 90], [0, 1], {
    extrapolateLeft: "clamp",
    extrapolateRight: "clamp",
  });
  const currentVotes = Math.round(votesRaw * VOTES-TARGET);

  // CTA button: scale in + glow, frames 70-85
  const ctaProgress = spring({
    frame: frame - 70,
    fps,
    config: { damping: 16, stiffness: 200 },
    durationInFrames: 15,
  });
  const ctaScale = interpolate(ctaProgress, [0, 0.7, 1], [0, 1.08, 1], {
    extrapolateRight: "clamp",
  });
  const ctaOpacity = interpolate(ctaProgress, [0, 0.3], [0, 1], {
    extrapolateRight: "clamp",
  });

  return (
    <AbsoluteFill
      style={{
        background: "#0f0f0f",
        justifyContent: "center",
        alignItems: "center",
      }}
    >
      {/* Card */}
      <div
        style={{
          transform: `scale(${cardScale})`,
          opacity: cardOpacity,
          background: "#ffffff",
          borderRadius: 16,
          padding: "36px 40px",
          width: 840,
          boxSizing: "border-box",
          fontFamily: "sans-serif",
          boxShadow: "0 32px 80px rgba(0,0,0,0.5)",
        }}
      >
        {/* Top section: logo + name/tagline */}
        <div
          style={{
            display: "flex",
            alignItems: "flex-start",
            gap: 24,
            marginBottom: 20,
          }}
        >
          {/* Product logo */}
          <div
            style={{
              transform: `scale(${logoScale})`,
              width: 80,
              height: 80,
              borderRadius: 18,
              background: "linear-gradient(135deg, #ff6154, #ff9f43)",
              flexShrink: 0,
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              fontSize: 38,
              boxShadow: "0 8px 24px rgba(255,97,84,0.4)",
            }}
          >
            🚀
          </div>

          {/* Name + tagline */}
          <div
            style={{
              transform: `translateX(${nameX}px)`,
              opacity: nameOpacity,
              flex: 1,
              paddingTop: 4,
            }}
          >
            <div
              style={{
                fontSize: 28,
                fontWeight: 800,
                color: "#1a1a2e",
                lineHeight: 1.2,
                marginBottom: 6,
              }}
            >
              {PRODUCT-NAME}
            </div>
            <div style={{ fontSize: 17, color: "#6b7280", lineHeight: 1.4 }}>
              {TAGLINE}
            </div>
          </div>
        </div>

        {/* Tags row */}
        <div
          style={{
            display: "flex",
            gap: 8,
            marginBottom: 20,
            flexWrap: "wrap",
          }}
        >
          {TAGS.map((tag, i) => (
            <div
              key={tag}
              style={{
                transform: `scale(${tagScales[i]})`,
                border: `1.5px solid ${PH-ORANGE}`,
                borderRadius: 20,
                padding: "4px 14px",
                fontSize: 14,
                color: PH-ORANGE,
                fontWeight: 600,
              }}
            >
              {tag}
            </div>
          ))}
        </div>

        {/* Description */}
        <div
          style={{
            fontSize: 16,
            color: "#374151",
            lineHeight: 1.7,
            marginBottom: 28,
            opacity: descOpacity,
          }}
        >
          {DESCRIPTION}
        </div>

        {/* Divider */}
        <div
          style={{
            height: 1,
            background: "#f3f4f6",
            marginBottom: 24,
            opacity: votesRaw,
          }}
        />

        {/* Stats row + CTA */}
        <div
          style={{
            display: "flex",
            alignItems: "center",
            justifyContent: "space-between",
          }}
        >
          {/* Stats */}
          <div
            style={{
              display: "flex",
              alignItems: "center",
              gap: 28,
              opacity: votesRaw > 0 ? 1 : 0,
            }}
          >
            {/* Vote count */}
            <div
              style={{
                display: "flex",
                flexDirection: "column",
                alignItems: "center",
              }}
            >
              <div
                style={{
                  fontSize: 13,
                  color: PH-ORANGE,
                  fontWeight: 700,
                  marginBottom: 2,
                }}
              >

              </div>
              <div
                style={{
                  fontSize: 32,
                  fontWeight: 900,
                  color: PH-ORANGE,
                  lineHeight: 1,
                }}
              >
                {currentVotes}
              </div>
              <div style={{ fontSize: 12, color: "#9ca3af", marginTop: 2 }}>

              </div>
            </div>

            {/* Comments */}
            <div
              style={{
                display: "flex",
                alignItems: "center",
                gap: 6,
                color: "#6b7280",
                fontSize: 16,
              }}
            >
              <span style={{ fontSize: 20 }}>💬</span>
              <span style={{ fontWeight: 600 }}>{COMMENTS} 則留言</span>
            </div>

            {/* Rank */}
            <div
              style={{
                display: "flex",
                alignItems: "center",
                gap: 6,
                color: "#6b7280",
                fontSize: 16,
              }}
            >
              <span style={{ fontSize: 20 }}>🥇</span>
              <span style={{ fontWeight: 600 }}>{RANK}</span>
            </div>
          </div>

          {/* CTA button */}
          <div
            style={{
              transform: `scale(${ctaScale})`,
              opacity: ctaOpacity,
              background: `linear-gradient(135deg, ${PH-ORANGE}, #ff9f43)`,
              borderRadius: 10,
              padding: "12px 28px",
              fontSize: 16,
              fontWeight: 700,
              color: "#ffffff",
              cursor: "pointer",
              boxShadow: `0 6px 20px rgba(255,97,84,0.4)`,
            }}
          >
            前往產品頁
          </div>
        </div>

        {/* Footer */}
        <div
          style={{
            marginTop: 24,
            paddingTop: 16,
            borderTop: "1px solid #f3f4f6",
            display: "flex",
            alignItems: "center",
            gap: 10,
            opacity: ctaOpacity,
          }}
        >
          <div
            style={{
              width: 22,
              height: 22,
              borderRadius: "50%",
              background: PH-ORANGE,
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              fontSize: 12,
              color: "#ffffff",
              fontWeight: 900,
            }}
          >
            P
          </div>
          <span style={{ fontSize: 14, color: "#9ca3af" }}>
            Product Hunt · 今日精選
          </span>
        </div>
      </div>
    </AbsoluteFill>
  );
};

登入後查看完整程式碼