// product-card-progressive-assemble — Progressive Assemble fields landing one by one (motion-lab final cut ported to native Remotion) // the detail card builds itself as if each field is grabbed into place: image → title → breadcrumb pills // popping in one by one → the price appears then gets demoted with a strikethrough, // while the accent-colored new price springs out → body text reveals line by line + accent highlight // blocks sweep left to right → swatches light up. // The whole card slowly pushes forward in scale to keep breathing. Design coordinates 480×270 (DesignStage scales proportionally). 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 frames → t // field landing style: rise = moves up 6px + fade in; pop = outBack bounces from 0.4 to 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)`, }; }; // description lines: [text, whether marker-highlighted] 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(); // whole-card breathing push const push = seg(t, 0, 0.75, E.outQuad); // price demotion (f=26): old price strikethrough, shrinks and turns gray; new price springs out const cut = t >= F(26); const nk = seg(t, F(26), F(26) + 0.12); return (
{/* left: product image placeholder (rises into place at f=0) */}
{/* zipper */}
{/* right column */}
{/* title (f=4) */}
Sample Product Title
{/* breadcrumb pills (one pop every 2 frames from f=8) */}
{['Category', 'Subgroup', 'Detail'].map((txt, i) => (
{txt}
))}
{/* price row (f=16): from f=26 the old price is demoted and the new price springs out */}
$249 $189
{/* description lines (one every 2 frames from f=30) + marker highlight (sweeps left to right from row f0+4) */}
{LINES.map((segs, li) => (
{segs.map(([txt, isMark], si) => !isMark ? ( {txt} ) : ( {txt} ), )}
))}
{/* swatches (one pop every 2 frames from f=40) */}
{['#191b20', '#8b8f99', '#3a5b8c'].map((cclr, i) => (
))}
); };