// product-card-progressive-assemble — Progressive Assemble 字段逐个落位(motion-lab 定稿转原生 Remotion) // 详情卡像被逐字段抓取般自建:图→标题→breadcrumb pill 依次 pop→价格出现后被划线降级、 // 强调色新价 spring 跳出→正文逐行揭示+强调色高亮块由左向右刷过→色卡点亮。 // 整卡极慢 scale 前推保持呼吸。设计坐标 480×270(DesignStage 等比放大)。 import React from 'react'; import { DesignStage, E, lerp, seg, useT } from '../../_fixtures/Motion'; export const PRODUCT_CARD_PROGRESSIVE_ASSEMBLE_DURATION = 150; // 5000ms @30fps const ACCENT = '#ff6a1f'; const ACCENT_SOFT = 'rgba(255,122,26,.42)'; const F = (f: number) => f / 60; // recipe 帧 → t // 字段落位样式:rise = 上移 6px 淡入;pop = outBack 从 0.4 弹到 1 const fieldStyle = (t: number, f0: number, mode: 'rise' | 'pop' = 'rise'): React.CSSProperties => { const k = seg(t, F(f0), F(f0) + 0.1, E.outCubic); return { opacity: Math.min(1, k * 2), transform: mode === 'pop' ? `scale(${lerp(E.outBack(Math.min(1, k)), 0.4, 1)})` : `translateY(${lerp(k, 6, 0)}px)`, }; }; // 描述行:[文本, 是否马克笔高亮] const LINES: [string, boolean?][][] = [ [['Placeholder copy for '], ['a key highlight', true], [' in the']], [['product body. '], ['Second highlight', true], [' sits here on']], [['the third line of neutral sample text.']], ]; export const ProductCardProgressiveAssemble: React.FC = () => { const t = useT(); // 整卡呼吸前推 const push = seg(t, 0, 0.75, E.outQuad); // 价格降级(f=26):旧价划线缩小变灰,新价 spring 跳出 const cut = t >= F(26); const nk = seg(t, F(26), F(26) + 0.12); return (
{/* 左:商品图占位(f=0 rise 落位) */}
{/* 拉链 */}
{/* 右列 */}
{/* 标题(f=4) */}
Sample Product Title
{/* breadcrumb pills(f=8 起每 2 帧一个 pop) */}
{['Category', 'Subgroup', 'Detail'].map((txt, i) => (
{txt}
))}
{/* 价格行(f=16):f=26 起旧价降级、新价 spring 跳出 */}
$249 $189
{/* 描述行(f=30 起每 2 帧一行)+ 马克笔高亮(行 f0+4 起由左向右刷过) */}
{LINES.map((segs, li) => (
{segs.map(([txt, isMark], si) => !isMark ? ( {txt} ) : ( {txt} ), )}
))}
{/* 色卡(f=40 起每 2 帧一个 pop) */}
{['#191b20', '#8b8f99', '#3a5b8c'].map((cclr, i) => (
))}
); };