敘事式網站設計
以書本圖示展示「敘事邏輯」概念,再轉場至仿瀏覽器的章節式網站版面 mockup,說明以敘事方式重新設計網站的思路。
網站設計敘事過場瀏覽器 mockup
提示詞(可直接修改內容)
import React from "react";
import {
AbsoluteFill,
useCurrentFrame,
useVideoConfig,
interpolate,
spring,
} from "remotion";
const colors = {
background: "#0A0E14",
backgroundGradient: "linear-gradient(135deg, #0A0E14 0%, #131A24 100%)",
warning: "#FFB547",
};
const fonts = { main: "'Inter', 'Noto Sans TC', sans-serif" };
export const NARRATIVE-REDESIGN-DURATION-FRAMES = 180;
export const Scene76-NarrativeRedesign: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const bookSpring = spring({
frame: Math.max(0, frame - 5),
fps,
config: { damping: 9, mass: 0.5, stiffness: 130 },
});
const bookScale = interpolate(bookSpring, [0, 1], [0.3, 1]);
const bookOpacity = interpolate(bookSpring, [0, 0.4], [0, 1], {
extrapolateRight: "clamp",
});
const textLineProgress = interpolate(frame, [15, 55], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
const bookGlow = interpolate(Math.sin(frame * 0.1), [-1, 1], [6, 18]);
const arrowProgress = interpolate(frame, [50, 80], [0, 1], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
const siteSpring = spring({
frame: Math.max(0, frame - 75),
fps,
config: { damping: 10, mass: 0.6, stiffness: 110 },
});
const siteScale = interpolate(siteSpring, [0, 1], [0.5, 1]);
const siteOpacity = interpolate(siteSpring, [0, 0.4], [0, 1], {
extrapolateRight: "clamp",
});
const badgeSpring = spring({
frame: Math.max(0, frame - 120),
fps,
config: { damping: 8, mass: 0.4, stiffness: 150 },
});
const badgeScale = interpolate(badgeSpring, [0, 1], [0.3, 1]);
const badgeOpacity = interpolate(badgeSpring, [0, 0.3], [0, 1], {
extrapolateRight: "clamp",
});
const fadeOut = interpolate(frame, [155, 180], [1, 0], {
extrapolateLeft: "clamp",
extrapolateRight: "clamp",
});
const showBook = frame < 85;
const showSite = frame >= 70;
return (
<AbsoluteFill style={{ background: colors.backgroundGradient }}>
<AbsoluteFill
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
opacity: fadeOut,
}}
>
{showBook && (
<div
style={{
position: "absolute",
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: 30,
opacity: bookOpacity * (frame >= 75 ? 1 - siteOpacity : 1),
transform: `scale(${bookScale})`,
}}
>
<div
style={{
borderRadius: 16,
border: `2.5px solid ${colors.warning}50`,
boxShadow: `0 0 ${bookGlow}px ${colors.warning}40`,
padding: 6,
}}
>
<svg width="390" height="330" viewBox="0 0 260 220">
<defs>
<linearGradient id="nvBookGrad" x1="0" y1="0" x2="1" y2="1">
<stop offset="0%" stopColor={colors.warning} stopOpacity="0.25" />
<stop offset="100%" stopColor="#FF8C42" stopOpacity="0.15" />
</linearGradient>
</defs>
<rect x="10" y="10" width="240" height="200" rx="12" fill="url(#nvBookGrad)" stroke={`${colors.warning}30`} strokeWidth="1.5" />
<rect x="125" y="10" width="10" height="200" rx="2" fill={colors.warning} opacity={0.5} />
{[0, 1, 2, 3, 4].map((i) => {
const lineOpacity = interpolate(textLineProgress, [i * 0.18, Math.min(1, i * 0.18 + 0.2)], [0, 1], { extrapolateLeft: "clamp", extrapolateRight: "clamp" });
return (
<rect key={i} x={22} y={35 + i * 30} width={i === 0 ? 80 * lineOpacity : 70} height={i === 0 ? 8 : 5} rx={i === 0 ? 3 : 2} fill={i === 0 ? colors.warning : "rgba(255,255,255,0.3)"} opacity={lineOpacity} />
);
})}
<circle cx="196" cy="170" r="24" fill={`${colors.warning}15`} stroke={`${colors.warning}40`} strokeWidth="1.5" />
<path d="M185 180 Q196 158 207 162 Q200 172 196 182 Z" fill={colors.warning} opacity={0.7} />
</svg>
</div>
<span style={{ fontSize: 45, fontWeight: 600, fontFamily: fonts.main, color: `${colors.warning}99`, letterSpacing: 2 }}>
敘事邏輯
</span>
</div>
)}
{arrowProgress > 0 && arrowProgress < 1 && (
<div style={{ position: "absolute", display: "flex", alignItems: "center", opacity: interpolate(arrowProgress, [0, 0.3, 0.7, 1], [0, 1, 1, 0]) }}>
<svg width="240" height="90" viewBox="0 0 160 60">
<line x1="10" y1="30" x2={10 + arrowProgress * 120} y2="30" stroke={colors.warning} strokeWidth="3" strokeLinecap="round" opacity={0.8} />
{arrowProgress > 0.7 && <path d="M130 22 L148 30 L130 38" fill="none" stroke={colors.warning} strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" opacity={interpolate(arrowProgress, [0.7, 1], [0, 1])} />}
</svg>
</div>
)}
{showSite && siteOpacity > 0 && (
<div
style={{
position: "absolute",
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: 39,
opacity: siteOpacity,
transform: `scale(${siteScale})`,
}}
>
<div style={{ borderRadius: 16, border: `2px solid ${colors.warning}35`, padding: 4 }}>
<svg width="750" height="540" viewBox="0 0 500 360">
<rect x="0" y="0" width="500" height="360" rx="12" fill="#0D1117" stroke={`${colors.warning}20`} strokeWidth="1.5" />
<rect x="0" y="0" width="500" height="26" rx="12" fill="rgba(255,255,255,0.04)" />
<circle cx="16" cy="13" r="4.5" fill="#FF5F57" opacity={0.6} />
<circle cx="31" cy="13" r="4.5" fill="#FFBD2E" opacity={0.6} />
<circle cx="46" cy="13" r="4.5" fill="#28CA41" opacity={0.6} />
<rect x="70" y="7" width="360" height="12" rx="6" fill="rgba(255,255,255,0.05)" />
<rect x="0" y="26" width="500" height="78" fill={colors.warning} opacity={0.08} />
<rect x="130" y="50" width="240" height="16" rx="5" fill={colors.warning} opacity={0.6} />
<rect x="160" y="72" width="180" height="9" rx="3" fill="rgba(255,255,255,0.18)" />
<rect x="205" y="88" width="90" height="12" rx="6" fill={colors.warning} opacity={0.4} />
<rect x="0" y="104" width="500" height="3" fill={`${colors.warning}20`} />
<rect x="18" y="125" width="215" height="10" rx="3" fill="rgba(255,255,255,0.12)" />
<rect x="18" y="141" width="195" height="7" rx="3" fill="rgba(255,255,255,0.07)" />
<rect x="0" y="182" width="500" height="3" fill={`${colors.warning}20`} />
{[0, 1, 2].map((i) => (
<g key={i}>
<rect x={18 + i * 162} y={205} width="148" height="60" rx="8" fill="rgba(255,255,255,0.03)" stroke={`${colors.warning}18`} strokeWidth="1" />
<circle cx={42 + i * 162} cy={225} r="10" fill={colors.warning} opacity={0.35} />
<text x={42 + i * 162} y={229} textAnchor="middle" fontSize="9" fontFamily={fonts.main} fill={colors.warning} fontWeight="700">{i + 1}</text>
</g>
))}
</svg>
</div>
{badgeOpacity > 0 && (
<div style={{ display: "flex", alignItems: "center", gap: 21, opacity: badgeOpacity, transform: `scale(${badgeScale})` }}>
<span style={{ fontSize: 54, fontWeight: 700, fontFamily: fonts.main, color: colors.warning, letterSpacing: 3 }}>
敘事式設計
</span>
</div>
)}
</div>
)}
</AbsoluteFill>
</AbsoluteFill>
);
};登入後查看完整程式碼