Acquisition / Retention / Referral
三張卡片依序彈入:Acquisition(怎麼讓人知道)、Retention(怎麼讓客人回來)、Referral(怎麼讓客人拉客人),各含對應的策略圖示。
行銷成長策略卡片
提示詞
我有一個用 Remotion 寫的場景元件(檔案:Scene141_AcqRetRef.tsx),請幫我做以下調整: 1. 修改三張卡片的標題(目前是 Acquisition / Retention / Referral) 2. 調整各卡片的副標題(目前是「怎麼讓人知道」、「怎麼讓客人回來」、「怎麼讓客人拉客人」) 3. 更換卡片顏色(目前是藍 #4DA3FF / 綠 #10B981 / 橘 #F59E0B) 4. 修改各卡片的策略項目(目前 Acq 有 4 項,Ret 有 3 項,Ref 有 3 項) 請保留原本的卡片彈入、頂部色條與圖示逐步出現動畫,只修改我指定的部分,然後把完整的修改後程式碼給我。
import React from "react";
import {
AbsoluteFill,
useCurrentFrame,
useVideoConfig,
interpolate,
spring,
} from "remotion";
const fonts = { main: "'Inter', 'Noto Sans TC', sans-serif" };
const MapPinIcon: React.FC<{ x: number; y: number }> = ({ x, y }) => (
<g>
<path d={`M ${x} ${y - 16} C ${x - 12} ${y - 16} ${x - 12} ${y - 2} ${x} ${y + 8}`} fill="none" stroke="#EF4444" strokeWidth={2} />
<path d={`M ${x} ${y - 16} C ${x + 12} ${y - 16} ${x + 12} ${y - 2} ${x} ${y + 8}`} fill="none" stroke="#EF4444" strokeWidth={2} />
<circle cx={x} cy={y - 8} r={4} fill="#EF4444" opacity={0.6} />
</g>
);
const NewsIcon: React.FC<{ x: number; y: number }> = ({ x, y }) => (
<g>
<rect x={x - 14} y={y - 14} width={28} height={28} rx={3} fill="none" stroke="#A78BFA" strokeWidth={2} />
<line x1={x - 8} y1={y - 6} x2={x + 8} y2={y - 6} stroke="#A78BFA" strokeWidth={2} strokeLinecap="round" />
<line x1={x - 8} y1={y + 1} x2={x + 4} y2={y + 1} stroke="#A78BFA" strokeWidth={1.5} strokeLinecap="round" opacity={0.5} />
<line x1={x - 8} y1={y + 7} x2={x + 8} y2={y + 7} stroke="#A78BFA" strokeWidth={1.5} strokeLinecap="round" opacity={0.5} />
</g>
);
const InfluencerIcon: React.FC<{ x: number; y: number }> = ({ x, y }) => (
<g>
<rect x={x - 10} y={y - 16} width={20} height={32} rx={4} fill="none" stroke="#EC4899" strokeWidth={2} />
<circle cx={x} cy={y - 2} r={5} fill="none" stroke="#EC4899" strokeWidth={1.5} />
<circle cx={x} cy={y - 2} r={2} fill="#EC4899" />
</g>
);
const BloggerIcon: React.FC<{ x: number; y: number }> = ({ x, y }) => (
<g>
<rect x={x - 16} y={y - 12} width={32} height={20} rx={2} fill="none" stroke="#10B981" strokeWidth={2} />
<line x1={x - 20} y1={y + 12} x2={x + 20} y2={y + 12} stroke="#10B981" strokeWidth={2.5} strokeLinecap="round" />
</g>
);
const StampCardIcon: React.FC<{ x: number; y: number }> = ({ x, y }) => (
<g>
<rect x={x - 16} y={y - 12} width={32} height={24} rx={3} fill="none" stroke="#10B981" strokeWidth={2} />
{[0, 1, 2, 3].map((j) => (
<circle key={j} cx={x - 9 + j * 6} cy={y} r={2.5} fill={j < 3 ? "#10B981" : "none"} stroke="#10B981" strokeWidth={1} />
))}
</g>
);
const SeasonalIcon: React.FC<{ x: number; y: number }> = ({ x, y }) => (
<g>
<path d={`M ${x} ${y - 12} L ${x + 4} ${y - 3} L ${x + 12} ${y - 3} L ${x + 6} ${y + 3} L ${x + 8} ${y + 12} L ${x} ${y + 7} L ${x - 8} ${y + 12} L ${x - 6} ${y + 3} L ${x - 12} ${y - 3} L ${x - 4} ${y - 3} Z`} fill="rgba(245,158,11,0.2)" stroke="#F59E0B" strokeWidth={2} strokeLinejoin="round" />
</g>
);
const GiftIcon: React.FC<{ x: number; y: number }> = ({ x, y }) => (
<g>
<rect x={x - 14} y={y - 6} width={28} height={20} rx={3} fill="none" stroke="#F59E0B" strokeWidth={2} />
<rect x={x - 16} y={y - 10} width={32} height={8} rx={2} fill="none" stroke="#F59E0B" strokeWidth={2} />
<line x1={x} y1={y - 10} x2={x} y2={y + 14} stroke="#F59E0B" strokeWidth={1.5} />
</g>
);
const GroupIcon: React.FC<{ x: number; y: number }> = ({ x, y }) => (
<g>
<circle cx={x - 8} cy={y - 8} r={6} fill="none" stroke="#F59E0B" strokeWidth={1.5} />
<circle cx={x + 8} cy={y - 8} r={6} fill="none" stroke="#F59E0B" strokeWidth={1.5} />
<circle cx={x} cy={y + 4} r={6} fill="none" stroke="#F59E0B" strokeWidth={1.5} />
</g>
);
const PhotoWallIcon: React.FC<{ x: number; y: number }> = ({ x, y }) => (
<g>
<rect x={x - 14} y={y - 14} width={28} height={28} rx={3} fill="none" stroke="#EC4899" strokeWidth={2} />
<circle cx={x - 4} cy={y - 4} r={4} fill="none" stroke="#EC4899" strokeWidth={1.5} />
<path d={`M ${x - 14} ${y + 8} L ${x - 4} ${y} L ${x + 4} ${y + 6} L ${x + 14} ${y - 2}`} fill="none" stroke="#EC4899" strokeWidth={1.5} strokeLinecap="round" />
</g>
);
const SharePostIcon: React.FC<{ x: number; y: number }> = ({ x, y }) => (
<g>
<rect x={x - 14} y={y - 12} width={28} height={24} rx={4} fill="none" stroke="#4DA3FF" strokeWidth={2} />
<path d={`M ${x + 4} ${y - 4} L ${x + 10} ${y} L ${x + 4} ${y + 4}`} fill="none" stroke="#4DA3FF" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round" />
<line x1={x - 8} y1={y} x2={x + 8} y2={y} stroke="#4DA3FF" strokeWidth={2} strokeLinecap="round" />
</g>
);
const CARDS = [
{
title: "Acquisition",
subtitle: "怎麼讓人知道",
color: "#4DA3FF",
delay: 10,
icons: [
{ Icon: MapPinIcon, zh: "Google Map" },
{ Icon: NewsIcon, zh: "新聞報導" },
{ Icon: InfluencerIcon, zh: "網紅分享" },
{ Icon: BloggerIcon, zh: "部落客推薦" },
],
},
{
title: "Retention",
subtitle: "怎麼讓客人回來",
color: "#10B981",
delay: 80,
icons: [
{ Icon: StampCardIcon, zh: "集點卡" },
{ Icon: SeasonalIcon, zh: "季節限定" },
{ Icon: GiftIcon, zh: "其他優惠" },
],
},
{
title: "Referral",
subtitle: "怎麼讓客人拉客人",
color: "#F59E0B",
delay: 145,
icons: [
{ Icon: GroupIcon, zh: "多人套餐" },
{ Icon: PhotoWallIcon, zh: "打卡牆" },
{ Icon: SharePostIcon, zh: "發文送小菜" },
],
},
];
export const Scene141_AcqRetRef: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const fadeIn = interpolate(frame, [0, 10], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
const fadeOut = interpolate(frame, [280, 300], [1, 0], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
const masterOpacity = Math.min(fadeIn, fadeOut);
const CARD_W = 530;
const CARD_H = 620;
const GAP = 45;
const totalW = CARD_W * 3 + GAP * 2;
const startX = (1920 - totalW) / 2;
const cardY = (1080 - CARD_H) / 2;
return (
<AbsoluteFill style={{ background: "linear-gradient(135deg, #0A0E14 0%, #111825 100%)", opacity: masterOpacity, fontFamily: fonts.main }}>
{CARDS.map((card, ci) => {
const cardSpring = spring({ frame: Math.max(0, frame - card.delay), fps, config: { damping: 12, stiffness: 80 } });
const left = startX + ci * (CARD_W + GAP);
return (
<div key={ci} style={{ position: "absolute", left, top: cardY, width: CARD_W, height: CARD_H, opacity: cardSpring, transform: `translateY(${(1 - cardSpring) * 40}px)` }}>
<div style={{ position: "absolute", width: "100%", height: "100%", borderRadius: 16, background: "rgba(255,255,255,0.03)", border: `1.5px solid ${card.color}33`, boxShadow: `0 8px 32px rgba(0,0,0,0.3)` }} />
<div style={{ position: "absolute", top: 0, left: 0, width: CARD_W * cardSpring, height: 4, background: card.color, borderRadius: "16px 16px 0 0", opacity: 0.7 }} />
<div style={{ position: "absolute", left: 28, top: 28, width: 44, height: 44, borderRadius: "50%", background: `${card.color}20`, border: `2px solid ${card.color}`, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 33, fontWeight: 700, color: card.color }}>
{ci + 1}
</div>
{(() => {
const titleSpring = spring({ frame: Math.max(0, frame - card.delay - 5), fps, config: { damping: 14, stiffness: 100 } });
return <div style={{ position: "absolute", left: 86, top: 24, opacity: titleSpring, fontSize: 54, fontWeight: 700, color: card.color, letterSpacing: 1 }}>{card.title}</div>;
})()}
{(() => {
const subSpring = spring({ frame: Math.max(0, frame - card.delay - 10), fps, config: { damping: 14, stiffness: 100 } });
return <div style={{ position: "absolute", left: 86, top: 88, opacity: subSpring * 0.5, fontSize: 33, fontWeight: 400, color: "#FFFFFF" }}>{card.subtitle}</div>;
})()}
<div style={{ position: "absolute", left: 28, top: 130, width: CARD_W - 56, height: 1, background: `${card.color}20` }} />
{card.icons.map((item, ii) => {
const iconSpring = spring({ frame: Math.max(0, frame - card.delay - 15 - ii * 8), fps, config: { damping: 12, stiffness: 100 } });
const iconY = 150 + ii * 120;
return (
<div key={ii} style={{ position: "absolute", left: 0, top: iconY, width: CARD_W, height: 100, opacity: iconSpring, transform: `translateX(${(1 - iconSpring) * 20}px)`, display: "flex", alignItems: "center" }}>
<div style={{ marginLeft: 40, width: 60, height: 60, display: "flex", alignItems: "center", justifyContent: "center" }}>
<svg width={75} height={75} viewBox="-25 -25 50 50">
<item.Icon x={0} y={0} />
</svg>
</div>
<div style={{ marginLeft: 16 }}>
<div style={{ fontSize: 39, fontWeight: 600, color: "rgba(255,255,255,0.75)", lineHeight: 1.2 }}>{item.zh}</div>
</div>
</div>
);
})}
</div>
);
})}
</AbsoluteFill>
);
};登入後查看完整程式碼