Motion · UI xuất hiện
Biến hình SVG lặp vòng
Một viền khép kín 140 điểm biến hình mượt sang viền khác rồi biến lại, hai hình trước hết được lấy mẫu lại cùng số điểm trong tọa độ cực, nội suy bán kính từng điểm + inOutCubic, giữa chừng chèn hơi thở scale nhẹ, tự xoay chậm và sắc màu trôi 185°→305°.
Nguồn: video-shotcraft (Vincent Wei) ↗ · Apache-2.0 · Composition Remotion (React/TSX) — có video preview, không có player trong trình duyệt
Khi nào dùng
Diễn tả khái niệm trừu tượng "hình thái biến đổi/thích ứng/sinh trưởng hữu cơ"; nhịp không khí trước logo mở đầu, hoặc hình chuyển tiếp giữa các chương
Ý đồ
"Hình thái sẽ biến đổi" — nếu chỉ dùng hai ảnh mờ dần chồng lên nhau thì không nói rõ được điều đó, khán giả thấy là hai thứ chồng lên nhau. Morph thật sự phải khiến khán giả tin đây luôn là cùng một thứ đang đổi hình dạng. Lấy mẫu lại cùng số điểm cộng nội suy từng điểm chính là bảo chứng kỹ thuật cho sự "đồng nhất" đó: không có điểm nào sinh ra hay mất đi, chỉ có bán kính thay đổi. Biến trở lại hình gốc là để khép vòng — khán giả thấy hành trình đi-về mới biết đây là năng lực có thể đảo ngược chứ không phải biến hình một lần.
Thời lượng & năng lượng
- Thời lượng: khoảng 5.2s (156f@30fps)
- Năng lượng: Thấp (dòng chảy liên tục không có đỉnh, hợp làm nền lời bình hoặc nhịp thở)
Công thức cảnh quay (bản gốc tiếng Trung — timing, easing, bảng tham số, lỗi hay gặp)
意图
"形态会变"这件事,用两张图交叉淡入是说不清的——观众看到的是两个东西重叠。真正的 morph 要让观众相信这一直是同一个东西在改变形状。等点数重采样加逐点插值就是这个 "同一性"的技术保证:没有点的生成或消失,只有半径在变。变回原形是为了闭环——观众 看到往返,才知道这是可逆的能力而不是一次性的变身。
动效核心
- 两形状用极坐标函数定义,不是路径字符串:
rA(θ) = 76*(1 + 0.30*cos(3θ) + 0.05*sin(7θ+0.8))、 rB(θ) = 76*(1 + 0.26*sin(5θ+1.2) + 0.06*cos(2θ))——三瓣形与五瓣形,各带一层高频 扰动做"有机"细节
- 重采样:两形状都在
i/140 * 2π上取样存进radA/radB数组,天生等点数且一一对应
——这就是 morphTo 需要的点数对齐,只不过在极坐标下免费获得
- 插值只发生在半径上:
r = lerp(m, radA[i], radB[i]),角度恒定。这保证插值中间态一定
是一条不自交的闭合曲线,不会出现路径插值常见的打结
- 往返用两条窗口相减:
m1 = seg(t, 0.08, 0.42, E.inOutCubic)(A→B)、
m2 = seg(t, 0.58, 0.92, E.inOutCubic)(B→A),m = m1 - m2——0 是 A、1 是 B, 中间 0.42→0.58 是 B 形态的静置段(约 25f)
- 呼吸与自转是叠加的第二层:
breath = 1 + 0.045*(sin(m1*π) + sin(m2*π))让缩放在两次
变形的中途各鼓一次(形状最不稳定时鼓得最多);rot = sin(t*2π)*4° 走一个整数 周期,末帧回零
- 色相跟形态绑定:
hue = lerp(m, 185, 305),同时驱动描边hsl(hue,90%,66%)、
填充 hsla(hue,80%,58%,.14) 与 drop-shadow 光晕——颜色是形态的读数,不是独立装饰
- 字幕在
m > 0.5时切换morphTo(shapeB)/morphTo(shapeA),透明度
0.4 + 0.6*|m-0.5|*2 在形态最"纯"时最亮、变形中途最淡
参数表
| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 采样点数 | N=140 | 140 点在 480×270 上足够平滑(L 直线段肉眼看不出);<60 点能看到多边形棱角,>300 点是纯浪费 | | 基半径 | BASE=76,中心 (240,138) | 76 相对 270 高度约占 56%——留边给光晕;调大要检查 0.30 的凸起是否顶出 viewBox | | 形状 A | 1 + 0.30cos(3θ) + 0.05sin(7θ+0.8) | 3θ 定瓣数、0.30 定凸起深度;深度 >0.5 时凹处半径趋近 0,形状会掐出尖角 | | 形状 B | 1 + 0.26sin(5θ+1.2) + 0.06cos(2θ) | 与 A 的瓣数不同(5 vs 3)才有变形张力;瓣数相同只会看到形状"转了转" | | 变形窗 | 0.08→0.42 / 0.58→0.92,均 inOutCubic | 行程 0.34(≈53f);inOut 的缓起缓收是"有机"的关键,换 linear 立刻读作机械插值 | | 中段静置 | 0.42→0.58(≈25f) | 这是让观众认清 B 形态的时间;砍掉会读作一次来回摆动而不是 A→B→A | | 呼吸幅度 | 0.045 × sin(m*π) | 4.5% 的呼吸在变形中途达峰;>10% 会读作缩放动画而不是形变 | | 自转 | sin(t*2π)*4°(1 个整数周期) | 4° 是暗示级别的漂移,末帧回零可循环;>15° 会看出是整体旋转,与形变抢注意力 | | 色相区间 | 185°→305°(青→紫) | 跨度 120° 让两个形态有明确的身份差;跨度 <40° 时颜色变化读不出,色相就白给了 |
已知坑
- 极坐标定义天生只能表达"从中心看每个角度一个半径"的形状——星形、花瓣、水滴都行,
但任何凹到自遮挡的形状(C 形、环形、有孔洞)都表达不出来。要 morph 真实 logo 轮廓必须换成路径重采样,这张卡的技术路线不适用
- 路径用
L直线段拼接而非曲线段,靠点数密度掩盖。如果做大幅放大(>2 倍)或极粗
描边,棱角会显形——放大场景要提高 N 或改用 C 段
- 每帧重建整条
d字符串(140 点字符串拼接 × 2 个 path)。480×270 无压力,但如果同屏
放多个实例或点数上到 500,要考虑缓存插值结果
- 两条形状函数写死在 setup 里,没有做成参数。要换形状是改函数体而不是传参——移植时
建议把 rA/rB 提成入参,否则每个变体都要复制一份 setup
- 字幕
morphTo(shapeB)是 anime.js API 的自我说明式占位文案,成片里应换成有语义的
文字或直接去掉
- 背景写死
#0a0b10,整套颜色(90% 饱和描边 + 8px 光晕)依赖深底。白底上光晕失效,
填充的 0.14 透明度也几乎不可见,浅色主题需要重配饱和度与描边宽度
参考实现
demos/ui-entrance/svg-shape-morph/ (SvgShapeMorph.tsx)
Mã nguồn tham chiếu (Remotion)
- demos/ui-entrance/svg-shape-morph/SvgShapeMorph.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/ui-entrance/svg-shape-morph/SvgShapeMorph.tsx
Component import fixture chung từ demos/_fixtures và helper từ assets/lib của repo; xem SKILL.md trong repo để biết quy trình render.
Mọi thứ trên trang này đều có bản máy đọc. Đưa agent link JSON, hoặc thêm MCP server gimgs rồi bảo nó tìm motion template.
- Tải JSON — trong đó có công thức cảnh quay (timing, easing, bảng tham số, lỗi hay gặp) và toàn bộ source TSX.
- Clone repo video-shotcraft; component import fixture chung từ demos/_fixtures và helper từ assets/lib.
- Đăng ký component thành <Composition> trong project Remotion rồi render bằng `npx remotion render`, hoặc dựng lại công thức bằng engine của bạn.
curl -s https://gimgs.net/motion/svg-shape-morph.jsongit clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/ui-entrance/svg-shape-morph/SvgShapeMorph.tsx
npx remotion render <CompositionId> out.mp4claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("svg-shape-morph")Prompt
Use the video-shotcraft shot recipe "Biến hình SVG lặp vòng" (Remotion composition, Apache-2.0) published on gimgs.net. Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/svg-shape-morph.json Raw TSX: https://gimgs.net/motion/svg-shape-morph/template.html Repository: https://github.com/Vincentwei1021/video-shotcraft (component: demos/ui-entrance/svg-shape-morph/SvgShapeMorph.tsx; imports demos/_fixtures and assets/lib) Spec: 1920x1080, 30 fps, 5.7s 1. Read the JSON and follow its "prompt" section (when to use, intention, parameter table, known pitfalls). 2. Adapt it for: <describe what you want>. 3. Either clone the repo, register the component as a <Composition> in a Remotion project and run npx remotion render <CompositionId> out.mp4 — or re-implement the recipe in your own engine (HTML/WAAPI works: keep the same timing and easing).
// ===== demos/ui-entrance/svg-shape-morph/SvgShapeMorph.tsx =====
// svg-shape-morph — Shape Morph 轮廓变形(motion-lab 定稿转原生 Remotion)
// 一个 SVG 轮廓平滑变形为另一个再变回:两条闭合轮廓先重采样到相同点数
// (极坐标 140 点),逐点插值 + inOutCubic,变形中段加轻微 scale 呼吸与
// 色相漂移,得到有机的流动感。
// 设计坐标 480×270(DesignStage 等比放大),参数表数值以此坐标系标定。
import React from 'react';
import { DesignStage, E, lerp, seg, useT } from '../../_fixtures/Motion';
export const SVG_SHAPE_MORPH_DURATION = 156; // 5200ms @30fps
const CX = 240;
const CY = 138;
const N = 140;
const BASE = 76;
// 两个形状:同一采样点数(等价 morphTo 的点数对齐)
const rA = (th: number) => BASE * (1 + 0.3 * Math.cos(th * 3) + 0.05 * Math.sin(th * 7 + 0.8));
const rB = (th: number) => BASE * (1 + 0.26 * Math.sin(th * 5 + 1.2) + 0.06 * Math.cos(th * 2));
const RAD_A: number[] = [];
const RAD_B: number[] = [];
for (let i = 0; i < N; i++) {
const th = (i / N) * Math.PI * 2;
RAD_A.push(rA(th));
RAD_B.push(rB(th));
}
// 按混合系数 m 逐点插值两条轮廓,拼 path d
const build = (m: number) => {
let d = '';
for (let i = 0; i < N; i++) {
const th = (i / N) * Math.PI * 2;
const r = lerp(m, RAD_A[i], RAD_B[i]);
const x = (CX + Math.cos(th) * r).toFixed(1);
const y = (CY + Math.sin(th) * r).toFixed(1);
d += (i ? 'L' : 'M') + x + ',' + y;
}
return d + 'Z';
};
export const SvgShapeMorph: React.FC = () => {
const t = useT();
const m1 = seg(t, 0.08, 0.42, E.inOutCubic); // A → B
const m2 = seg(t, 0.58, 0.92, E.inOutCubic); // B → A
const m = m1 - m2; // 0=A 1=B
const d = build(m);
const hue = lerp(m, 185, 305);
// 变形中段的 scale 呼吸 + 缓慢自转
const breath = 1 + 0.045 * (Math.sin(m1 * Math.PI) + Math.sin(m2 * Math.PI));
const rot = Math.sin(t * Math.PI * 2) * 4;
return (
<DesignStage bg="#0a0b10">
<svg
viewBox="0 0 480 270"
style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', background: '#0a0b10' }}
>
<g transform={`translate(${CX},${CY}) scale(${breath}) rotate(${rot}) translate(${-CX},${-CY})`}>
<path d={d} fill={`hsla(${hue},80%,58%,.14)`} />
<path
d={d}
fill="none"
strokeWidth={2}
strokeLinejoin="round"
stroke={`hsl(${hue},90%,66%)`}
style={{ filter: `drop-shadow(0 0 8px hsla(${hue},90%,60%,.4))` }}
/>
</g>
<text
x={CX}
y={252}
textAnchor="middle"
fill="#5b6480"
opacity={0.4 + 0.6 * Math.abs(m - 0.5) * 2}
style={{ font: '10px "SF Mono",Menlo,monospace', letterSpacing: '2px' }}
>
{m > 0.5 ? 'morphTo(shapeB)' : 'morphTo(shapeA)'}
</text>
</svg>
</DesignStage>
);
};
- Read the recipePrompt = when to use, intention, parameter table with typical values and how each one feels, known pitfalls. Treat values as calibrated starting points.
- Get the codetool: git
git clone https://github.com/Vincentwei1021/video-shotcraft && cd video-shotcraft && npm install - Register + rendertool: remotion
npx remotion render <CompositionId> out.mp4 # component: demos/ui-entrance/svg-shape-morph/SvgShapeMorph.tsx - Or re-implementPort the timing/easing from the recipe to HTML + WAAPI (gimgs motion contract) or any engine; keep the same segment windows and easing.
| param | type | default | description |
|---|---|---|---|
| — | |||
Cùng danh mục

Hiện hình cả bức tường: ba kiểu
Ba kiểu vào hình hàng loạt cả bức tường — bento thắp sáng từng ô, lưới lật sóng, và bản vẽ blueprint hiện nét; tất cả hiện hình tại chỗ không dịch chuyển, bổ trợ cho deck-deal-flyin kiểu bay vào dịch chuyển.

Cột nhập theo bậc thang giá trị
16 cột vào hình với delay là bậc thang thời gian, đồng thời chiều cao/sắc màu/dịch chuyển/độ mờ mỗi thuộc tính tự trải thành bậc thang giá trị từ đầu đến cuối; nhịp hai đổi gốc lệch nhịp về tâm, biên độ xung lấy tâm làm đỉnh trải lại.

Hiện hình từ khung xương
Hiện hình ba tầng từ phác thảo đến khung xương đến nội dung — nét vẽ tay được thay bằng thanh xám khung xương rồi rõ nét thành UI thật.

Thẻ UI đổ mưa lướt sát mặt đất
Từ góc máy thấp lướt sát mặt đất, thẻ UI đổ như mưa rào rồi dừng cứng không nảy, sau đó cả trang dựng lên và quay thẳng vào camera.