LP Hero Video Skill
在登陸頁面或網站中有效嵌入產品展示影片的指南。
支援模式
- Hero Background Video — 頁面頂部的全螢幕背景影片
- Product Demo Section — 產品介紹區塊中的內嵌影片
- Floating Device Mockup — 在裝置框架內顯示影片
- Lightbox / Modal Video — 點擊後放大播放
Step 0: 事前確認檢查清單
在嵌入影片前,請確認以下事項:
影片素材的確認
- 是否已準備好影片檔案?(如果尚未準備,請先建立佔位符UI)
- 格式:理想為
.mp4(H.264)+.webm(VP9)兩種格式 - 解析度:Hero建議使用 1920x1080,卡片內使用 1280x720 即可
- 檔案大小:Hero建議 5-15MB,卡片內建議 2-5MB
- 長度:自動播放以 10-30秒的循環為佳
影片素材的製作方法(使用Screen Studio等工具錄製時)
- 錄製產品最令人印象深刻的操作流程,長度 15-30秒
- 以 1920x1080 以上的解析度錄製
- 匯出時設定如下:
- MP4 (H.264):品質 Medium-High、位元率 4-8 Mbps
- WebM (VP9):以相同品質額外匯出(若工具支援)
- 使用 ffmpeg 的轉換指令:
# MP4 最佳化(網頁用,附 faststart)
ffmpeg -i input.mov -c:v libx264 -crf 23 -preset slow \
-movflags +faststart -an -vf scale=1920:-2 output.mp4
# WebM 轉換
ffmpeg -i input.mov -c:v libvpx-vp9 -crf 30 -b:v 0 \
-an -vf scale=1920:-2 output.webm
# 卡片用(較小尺寸)
ffmpeg -i input.mov -c:v libx264 -crf 26 -preset slow \
-movflags +faststart -an -vf scale=1280:-2 output-card.mp4
影片託管
- 本地(
/public/videos/):適用於小型網站、Vercel / Netlify 部署 - CDN / Object Storage:適用於大型網站或多個影片(Cloudflare R2, AWS S3 + CloudFront)
- YouTube / Vimeo embed:適合重視SEO或需要外部共享的場合
Pattern 1: Hero Background Video
在頁面頂部全螢幕顯示產品展示影片,並附帶文字疊加層。
React 元件
interface HeroVideoProps {
/** MP4 影片路徑 */
videoSrc: string;
/** WebM 影片路徑(可省略) */
videoSrcWebm?: string;
/** 影片載入前顯示的海報圖片 */
posterSrc?: string;
/** 疊加層的子元件(如標題) */
children?: React.ReactNode;
/** 疊加層的暗度 (0-1, 預設 0.4) */
overlayOpacity?: number;
}
function HeroVideo({
videoSrc,
videoSrcWebm,
posterSrc,
children,
overlayOpacity = 0.4,
}: HeroVideoProps) {
const videoRef = useRef<HTMLVideoElement>(null);
return (
<div className="relative h-screen w-full overflow-hidden bg-black">
{/* Background Video */}
<video
ref={videoRef}
autoPlay
muted
loop
playsInline
poster={posterSrc}
className="absolute inset-0 w-full h-full object-cover"
>
{videoSrcWebm && <source src={videoSrcWebm} type="video/webm" />}
<source src={videoSrc} type="video/mp4" />
</video>
{/* Dark Overlay */}
<div
className="absolute inset-0"
style={{ backgroundColor: `rgba(0, 0, 0, ${overlayOpacity})` }}
/>
{/* Gradient fade to page background */}
<div className="absolute inset-x-0 bottom-0 h-32 bg-gradient-to-t from-background to-transparent" />
{/* Content */}
{children && (
<div className="absolute inset-0 z-10 flex items-end">
<div className="container pb-16 md:pb-24">{children}</div>
</div>
)}
</div>
);
}
使用範例
<HeroVideo
videoSrc="/videos/product-demo.mp4"
videoSrcWebm="/videos/product-demo.webm"
posterSrc="/images/hero-poster.jpg"
overlayOpacity={0.35}
>
<h1 className="text-4xl md:text-6xl font-normal text-white tracking-tight">
用科技,重新發明對話。
</h1>
</HeroVideo>
在 Plural Reality LP 中的應用位置
取代或與現有的 LifeGameHero 元件共存:
// Home.tsx 中的置換模式
// Option A: 完全取代
<HeroVideo videoSrc="/videos/hero-demo.mp4" posterSrc="/images/IMG_5298.jpeg">
<h1 ...>{h.heroTitle}</h1>
</HeroVideo>
// Option B: 保留 LifeGameHero,並在其下方新增影片區塊
<LifeGameHero>...</LifeGameHero>
<Section className="py-0">
<HeroVideo videoSrc="/videos/product-overview.mp4" overlayOpacity={0.2}>
<h2>產品介紹</h2>
</HeroVideo>
</Section>
Pattern 2: Product Card Video
在產品列表的每個卡片中,以滑鼠懸停播放展示影片取代靜態圖片。
React 元件
interface ProductCardVideoProps {
videoSrc: string;
posterSrc: string;
alt: string;
className?: string;
}
function ProductCardVideo({
videoSrc,
posterSrc,
alt,
className,
}: ProductCardVideoProps) {
const videoRef = useRef<HTMLVideoElement>(null);
const handleMouseEnter = () => {
videoRef.current?.play();
};
const handleMouseLeave = () => {
const video = videoRef.current;
if (video) {
video.pause();
video.currentTime = 0;
}
};
return (
<div
className={cn("relative aspect-video bg-muted overflow-hidden", className)}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
<video
ref={videoRef}
muted
loop
playsInline
poster={posterSrc}
preload="metadata"
className="w-full h-full object-cover"
>
<source src={videoSrc} type="video/mp4" />
</video>
</div>
);
}
使用範例(取代現有卡片圖片)
// Before:
<ParallaxImage src="/images/kouchoai.png" alt={t.products.kouchouAI} />
// After:
<ProductCardVideo
videoSrc="/videos/kouchou-ai-demo.mp4"
posterSrc="/images/kouchoai.png"
alt={t.products.kouchouAI}
/>
Pattern 3: Device Mockup Video
在瀏覽器或手機框架內顯示展示影片(提升信賴感)。
function BrowserMockupVideo({
videoSrc,
posterSrc,
title,
}: {
videoSrc: string;
posterSrc?: string;
title?: string;
}) {
return (
<div className="border border-border bg-background overflow-hidden">
{/* Browser chrome */}
<div className="flex items-center gap-2 px-4 py-3 bg-secondary/30 border-b border-border">
<div className="flex gap-1.5">
<div className="w-3 h-3 rounded-full bg-muted-foreground/20" />
<div className="w-3 h-3 rounded-full bg-muted-foreground/20" />
<div className="w-3 h-3 rounded-full bg-muted-foreground/20" />
</div>
{title && (
<div className="flex-1 text-center">
<span className="text-xs text-muted-foreground font-mono">{title}</span>
</div>
)}
</div>
{/* Video content */}
<div className="aspect-video">
<video
autoPlay
muted
loop
playsInline
poster={posterSrc}
className="w-full h-full object-cover"
>
<source src={videoSrc} type="video/mp4" />
</video>
</div>
</div>
);
}
Pattern 4: Lightbox Modal Video
點擊縮圖後以模態視窗顯示影片,也支援 YouTube 嵌入。
function VideoLightbox({
thumbnailSrc,
videoSrc,
youtubeId,
alt,
}: {
thumbnailSrc: string;
videoSrc?: string;
youtubeId?: string;
alt: string;
}) {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<button
onClick={() => setIsOpen(true)}
className="relative group cursor-pointer aspect-video w-full overflow-hidden"
>
<img src={thumbnailSrc} alt={alt} className="w-full h-full object-cover" />
{/* Play button overlay */}
<div className="absolute inset-0 flex items-center justify-center bg-black/20 group-hover:bg-black/30 transition-colors">
<div className="w-16 h-16 flex items-center justify-center bg-white/90 group-hover:bg-white transition-colors">
<svg className="w-6 h-6 text-foreground ml-1" fill="currentColor" viewBox="0 0 24 24">
<path d="M8 5v14l11-7z" />
</svg>
</div>
</div>
</button>
{/* Modal */}
{isOpen && (
<div
className="fixed inset-0 z-50 flex items-center justify-center bg-black/80"
onClick={() => setIsOpen(false)}
>
<div className="w-full max-w-5xl aspect-video" onClick={(e) => e.stopPropagation()}>
{youtubeId ? (
<iframe
src={`https://www.youtube.com/embed/${youtubeId}?autoplay=1`}
className="w-full h-full"
allow="autoplay; encrypted-media"
allowFullScreen
/>
) : (
<video
src={videoSrc}
autoPlay
controls
className="w-full h-full object-contain bg-black"
/>
)}
</div>
</div>
)}
</>
);
}
實作檢查清單
新增影片時請確認以下項目:
效能
- 設定
autoPlay muted loop playsInline四個屬性(iOS 必須) - 使用
poster屬性指定初始幀圖片(提升載入體驗) - 卡片內影片使用
preload="metadata"以減輕初始載入負擔 - 影片檔案已套用
-movflags +faststart(支援漸進式播放) - 在
<source>中提供 MP4 + WebM 兩種格式(瀏覽器相容性)
響應式
- 使用
object-cover/object-contain適當調整顯示方式 - 在行動裝置上考慮以靜態圖片取代影片
const isMobile = window.innerWidth < 768; // 行動裝置上僅顯示 poster 圖片,不載入影片
無障礙
- 自動播放影片必須加上
muted(未經使用者操作的聲音播放會被封鎖) - 支援
prefers-reduced-motion,在動畫停用時顯示靜態圖片作爲備援@media (prefers-reduced-motion: reduce) { video { display: none; } .video-poster-fallback { display: block; } }
設計(遵循 Plural Reality)
- 無圓角(
border-radius: 0px) - 邊框:
1px solid var(--border) - 疊加層使用黑色漸層(維持單色調)
- 播放按鈕圖示使用 Lucide React 的
Play
檔案配置慣例
client/
public/
videos/
hero-demo.mp4 # 英雄影片(MP4)
hero-demo.webm # 英雄影片(WebM)
hero-poster.jpg # 英雄影片海報圖片
kouchou-ai-demo.mp4 # 廣聽AI 展示影片
baisoku-survey-demo.mp4 # 倍速調查 展示影片
baisoku-kaigi-demo.mp4 # 倍速會議 展示影片
src/
components/
HeroVideo.tsx # 英雄全螢幕影片元件
ProductCardVideo.tsx # 卡片內懸停播放元件
BrowserMockupVideo.tsx # 附瀏覽器框架的影片
VideoLightbox.tsx # 模態影片元件
常見配置範例
SaaS / 產品登陸頁(建議配置)
Hero全螢幕影片(15-30秒循環、靜音自動播放)
↓
文字區塊(使命與價值)
↓
瀏覽器模擬影片(主要產品展示)
↓
產品卡片(懸停時播放個別展示影片)
↓
案例研究 / 成果展示
↓
CTA
公司官網(低調配置)
英雄圖片 + 播放按鈕(點擊後以Lightbox播放影片)
↓
文字區塊
↓
產品卡片(靜態圖片,詳細頁面內有影片)
Pattern 5: 使用CSS動畫的產品UI模擬(建議)
無需影片檔案,直接使用CSS動畫 / SVG / Canvas繪製產品的操作感。 Stripe、Linear、Vercel 都採用這種方式。
優點
- 無需影片檔案(零額外位元組)
- 完全響應式(在任何螢幕尺寸下都不會變形)
- 原生渲染,畫質清晰(無壓縮失真)
- 可支援互動(懸停、點擊等變化)
- 自動支援深色模式/淺色模式
前提:現有動畫資產
Plural Reality LP 的 SolutionAnimations.tsx 中已實作以下高品質動畫:
| 元件 | 內容 | 技術 |
|---|---|---|
NetworkOntologyAnimation | 3層本體論(DATA→ONTOLOGY→APPLICATION) | SVG + framer-motion |
DataFlowAnimation | 收集→整理→串接 管道 | SVG + framer-motion |
ClusterAnimation | 意見分群視覺化 | SVG + framer-motion |
ConversationBranchAnimation | 深度訪談的分支樹 | SVG + framer-motion |
FeedbackLoopAnimation | 持續對話循環 | SVG + framer-motion |
IntegrationMeshAnimation | 平台整合圖 | SVG + framer-motion |
ParticleFieldAnimation | 粒子網路 | Canvas |
IsometricLayersAnimation | 等角投影圖層結構 | SVG + framer-motion |
此外,LifeGameHero.tsx 中有地球 → 日本的 Canvas 動畫。
共通設計原則
- 背景:黑色(#000000),以白色元素繪製
- 單色調(不使用彩色,或僅用 Brand Teal 作爲強調色)
- 字體:技術標籤使用 var(--font-mono),UI元素使用 var(--font-sans)
- 無圓角:rx={0} / border-radius: 0px
- 動畫:使用 framer-motion 的 useInView + motion.* 實現滾動觸發
- 網格背景:淺白線網格模式(GridBg 工具)
- 發光:使用 feDropShadow 實現低調發光(GlowDef 工具)
Pattern 5A: 瀏覽器框架 + 動畫UI
在瀏覽器外框(標題列 + 圓點)中顯示動畫模擬UI。
function ProductDemoMock({
title,
children,
className,
}: {
title: string;
children: React.ReactNode;
className?: string;
}) {
return (
<div className={cn("border border-border bg-black overflow-hidden", className)}>
{/* Browser chrome */}
<div className="flex items-center gap-2 px-4 py-3 bg-[#1c1c1e] border-b border-border">
<div className="flex gap-1.5">
<div className="w-2.5 h-2.5 rounded-full bg-white/10" />
<div className="w-2.5 h-2.5 rounded-full bg-white/10" />
<div className="w-2.5 h-2.5 rounded-full bg-white/10" />
</div>
<div className="flex-1 text-center">
<span className="text-[10px] text-white/40 font-mono">{title}</span>
</div>
</div>
{/* Animated content */}
<div className="aspect-video relative overflow-hidden">
{children}
</div>
</div>
);
}
Pattern 5B: 廣聽AI 展示動畫
文字資料流入 → AI 進行分群 → 顯示結果的流程。
// 概念設計(實作時參考SolutionAnimations的ClusterAnimation)
function KouchouAIDemoAnimation() {
const [phase, setPhase] = useState<"input" | "processing" | "result">("input");
useEffect(() => {
const timers = [
setTimeout(() => setPhase("processing"), 2000),
setTimeout(() => setPhase("result"), 4000),
];
const loop = setInterval(() => {
setPhase("input");
timers.push(setTimeout(() => setPhase("processing"), 2000));
timers.push(setTimeout(() => setPhase("result"), 4000));
}, 8000);
return () => { timers.forEach(clearTimeout); clearInterval(loop); };
}, []);
return (
<ProductDemoMock title="kouchou-ai.plural-reality.com">
<svg viewBox="0 0 800 450" className="w-full h-full">
{/* Phase 1: 文字行從左側滑入 */}
{phase === "input" && (
<g>
{["交通不便令人困擾", "希望加強育兒支援", "擔心防災措施"].map((text, i) => (
<motion.g
key={text}
initial={{ opacity: 0, x: -40 }}
animate={{ opacity: 1, x: 0 }}
transition={{ delay: i * 0.4 }}
>
<rect x={40} y={60 + i * 50} width={350} height={36} fill="white" fillOpacity={0.05} stroke="white" strokeWidth={0.5} />
<text x={56} y={82 + i * 50} fill="white" fontSize={11} fontFamily="var(--font-sans)" opacity={0.8}>
{text}
</text>
</motion.g>
))}
</g>
)}
{/* Phase 2: 粒子向中央聚集 */}
{phase === "processing" && (
<motion.text x={400} y={225} textAnchor="middle" fill="white" fontSize={12} fontFamily="var(--font-mono)"
initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
正在分析中...
</motion.text>
)}
{/* Phase 3: 顯示分群結果 */}
{phase === "result" && (
<g>
{[{label: "交通議題", x: 200}, {label: "育兒議題", x: 400}, {label: "防災議題", x: 600}].map((cluster) => (
<motion.g key={cluster.label}
initial={{ scale: 0, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
transition={{ type: "spring", stiffness: 100, damping: 15 }}
>
<circle cx={cluster.x} cy={225} r={60} fill="white" fillOpacity={0.05} stroke="white" strokeWidth={0.5} />
<text x={cluster.x} y={225} textAnchor="middle" dominantBaseline="middle" fill="white" fontSize={12} fontFamily="var(--font-mono)">
{cluster.label}
</text>
</motion.g>
))}
</g>
)}
</svg>
</ProductDemoMock>
);
}
提供建議時的注意事項
應提案使用CSS動畫(Pattern 5)的時機
- 實現簡單的操作流程(點擊、輸入、結果顯示等2-3步驟)
- 需要深色模式/淺色模式雙支援
- 需要回應使用者互動(懸停、點擊)
- 設計稿已準備好(尤其是使用Figama UI模擬)
- 團隊對framer-motion有經驗
應提案使用影片的時機
- 需要實際錄影的複雜UI操作(多次點擊、拖拽等)
- 包含實際資料或地圖顯示
- 需要在多個裝置(實際設備)上確認動作
- 沒有設計動畫的時間,需要快速交付
設計的一致性
無論選擇哪種Pattern,以下要點務必統一:
- 邊框、背景色遵循Design Token
- 無圓角、單色調
- 使用相同的瀏覽器框架元件
- 所有動畫使用framer-motion(統一時間軸)
Pattern 5B 實作:廣聽AI 展示動畫(完整版)
以下是在 SolutionAnimations.tsx 中新增的實作方案。
// 在 SolutionAnimations.tsx 中新增
export function KouchouAIDemoAnimation() {
const [phase, setPhase] = useState<0 | 1 | 2>(0);
// 循環動畫
useEffect(() => {
const sequence = async () => {
while (true) {
setPhase(0);
await new Promise((r) => setTimeout(r, 3000));
setPhase(1);
await new Promise((r) => setTimeout(r, 3000));
setPhase(2);
await new Promise((r) => setTimeout(r, 4000));
}
};
sequence();
}, []);
return (
<ProductDemoMock title="kouchou-ai.plural-reality.com">
<svg viewBox="0 0 800 450" className="w-full h-full">
{/* 背景網格 */}
<GridBg />
{/* Phase 0: 文字輸入 */}
{phase === 0 && <InputPhase />}
{/* Phase 1: 處理中 */}
{phase === 1 && <ProcessingPhase />}
{/* Phase 2: 結果顯示 */}
{phase === 2 && <ResultPhase />}
</svg>
</ProductDemoMock>
);
}
這將在 PatternsShowcase 或產品介紹區塊中以 scroll-triggered 方式顯示。