// ===== demos/outro/grain-dissolve/GrainDissolve.tsx =====
// grain-dissolve — Grain Dissolve → Condense 文字砂化凝聚(motion-lab 定稿转原生 Remotion)
// 干净的整行字 "{ ACME. Now Live }" 先爆裂成沸腾颗粒噪点(轮廓隐约可辨、白色辉光),
// 同时出现带 45° 斜纹填充和像素方块角柄的选区框;噪点沸腾约半程后选区框消失,
// 噪点云急速凝聚成更大号的颗粒短字标(占位词 "ACME"),位移量衰减归零、辉光冲高回落,
// 凝固为清晰发光短字标。四角 HUD 括角/圆点与左右中线短划全程常驻。
// 滤镜链:feTurbulence seed 逐帧 + displacement scale 双向动画,终字同走滤镜再解除。
// 设计坐标 480×270(DesignStage 等比放大),SVG viewBox 640×360 铺满全幅。
import React, { useId } from 'react';
import { E, DesignStage, seg, useT } from '../../_fixtures/Motion';
export const GRAIN_DISSOLVE_DURATION = 60; // 2000ms @30fps
// 选区框几何(viewBox 坐标)
const BX = 128;
const BY = 148;
const BW = 384;
const BH = 62;
// 45° 斜纹:x 从 bx-bh 起每 34 一根,右下→左上
const HATCH_XS: number[] = [];
for (let x = BX - BH; x < BX + BW; x += 34) HATCH_XS.push(x);
// 四角像素棋盘手柄(两块 5×5 错位方块)
const Handle: React.FC<{ x: number; y: number }> = ({ x, y }) => (
);
// HUD 括角 + 圆点(sx/sy 控制朝向)
const Corner: React.FC<{ x: number; y: number; sx: number; sy: number }> = ({ x, y, sx, sy }) => (
<>
>
);
export const GrainDissolve: React.FC = () => {
const t = useT();
// 滤镜/clipPath ID 按实例生成,同一 Composition 放多个实例时互不串引
// (useId 的 «:» 在 CSS url() 里非法,清洗成纯字母数字)
const uid = useId().replace(/[^a-zA-Z0-9]/g, '');
const fid = `gd-${uid}`;
const cid = `gd-${uid}-clip`;
const burst = seg(t, 0.13, 0.28, E.outCubic); // 干净字 → 砂化
const cond = seg(t, 0.60, 0.71, E.inOutCubic); // 整行噪点云 → 短字标噪点云
const lock = seg(t, 0.68, 0.90, E.outCubic); // 位移衰减凝固
const settle = seg(t, 0.88, 1, E.outCubic); // 辉光回落
// 白色辉光:砂化期轻微,凝聚时冲高,凝固后回落到柔光
const glow = burst * 0.3 + cond * 0.7 - settle * 0.45;
return (
);
};