Motion · Ánh sáng & nhấn

Sóng tròn quanh điện thoại và chip

Bốn vòng tròn đồng tâm thở lệch pha như gợn nước trên nền xám nhạt, điện thoại mockup tự cuộn feed, chip trắng hai bên spring bật vào.

1920×108016:930 fps6.1s · 183 frameremotionGiấy phép: Apache-2.0#shotcraft#remotion#effects

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

Cảnh đứng hình "đây chính là nó" cho sản phẩm di động; đoạn giới thiệu với điểm tính năng xếp hai bên; toàn cảnh sản phẩm ở đầu/cuối phim

Ý đồ

Đặt điện thoại chính giữa màn hình bằng ít động tác nhất và khiến nó "sống": vòng tròn đồng tâm lo phần thở của bầu không khí, feed tự cuộn trong màn hình chứng minh "bên trong có nội dung đang chạy", chip hai bên chỉ ra tên tính năng. Cả ba lớp chuyển động đều cực chậm khi đứng riêng — chồng lại với nhau mới không bị đứng hình. Đây là công thức chuẩn cho cảnh đứng hình sản phẩm.

Thời lượng & năng lượng

  • Thời lượng: Khoảng 5.6s (168f@30fps)
  • Năng lượng: Thấp (tĩnh lặng, có nhịp thở, dựa vào sự nhấp nhô liên tục của vòng tròn đồng tâm để không bị nguội cảnh)

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)

意图

用最少的动作把手机放在画面正中并让它"活着":同心圆负责氛围呼吸, 屏内自动滚屏负责证明"里面有内容在跑",两侧 chip 负责点出功能名。 三层动作各自极慢,叠起来才不会显得静止——这是产品定格镜头的标准配法。

动效核心

  • 4 层同心圆(560/440/320/210px,#d7dbe1→#eef0f3 由深到浅)各自

scale 1 ± 0.06,全片走 1.5 个正弦周期,相邻层相位差 1.7 rad ——错相是"水波"而不是"整体缩放"的关键

  • 手机 mockup 132×264、22px 圆角、0 24px 50px rgba(58,64,74,.35)

的落地投影,屏内 8 张骨架卡组成 feed

  • feed translateY 0→−150px,窗口 t 0.08→0.98 匀速无缓动
  • chip pop:seg(t0, t0+0.12, outBack) 驱动 scale 0.8→1,opacity 在

前 0.08 就跳满;左 chip 起 0.22、右 chip 起 0.40

  • pop 完接 sin 悬浮 ±3px(2 周期/全片),并由 seg(t0+0.12, t0+0.3)

渐渐介入——不是一落位就开始飘

参数表

| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 同心圆层数/尺寸 | 4 层 560/440/320/210px | 3 层撐不满画面,5 层以上最外层被裁掉只剩色带 | | 呼吸幅度 | scale 1±0.06,1.5 周期/全片 | 0.06 已是上限;到 0.1 就读作"脉冲动画"而非水波 | | 相位差 | 相邻层 1.7 rad | 相位全同 = 整块放大缩小;差 1.7 才有波从中心荡开的错觉 | | 手机投影 | 0 24px 50px rgba(58,64,74,.35) | 投影是手机"浮在波上"的唯一依据,去掉就贴平了 | | 自动滚屏 | translateY 0→−150px,t 0.08→0.98 匀速 | 加 ease 立刻读作"有人在滑";匀速才是自动播放 | | chip pop | outBack、0.12 时长、scale 0.8→1 | outBack 的过冲量是"弹出"的全部质感,换 outCubic 就平了 | | chip 时序 | 左 0.22 / 右 0.40(≈0.6s 差) | 同时出现读作两个标签;错开才有"一个一个说"的节奏 | | chip 悬浮 | ±3px、2 周期,pop 后 0.18 内渐入 | 幅度超过 5px 就和同心圆的呼吸抢戏 |

已知坑

  • chip 用 calc(50% + 86px) 贴着手机边缘定位;改手机宽度必须同步改

这个 86px,否则 chip 压到 mockup 上

  • 屏内 feed 是 8 张骨架卡的占位内容,落地换真实截图时总高必须

大于「150px + 屏高」,否则滚到底露白

  • 同心圆是实色叠放(不是描边圆环),层序必须由大到小 append,

顺序反了就只看得见最大那层

  • ACCENT_RGB(默认 122,134,153 中性灰蓝)只作用于屏内缩略图渐变,

换项目品牌色只改这一个变量即可换肤

参考实现

demos/effects/radial-ripple-phone-chips/ (RadialRipplePhoneChips.tsx)

Mã nguồn tham chiếu (Remotion)

  • demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.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.

  1. 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.
  2. Clone repo video-shotcraft; component import fixture chung từ demos/_fixtures và helper từ assets/lib.
  3. Đă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/radial-ripple-phone-chips.json
git clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.tsx
npx remotion render <CompositionId> out.mp4
claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("radial-ripple-phone-chips")

Prompt

Use the video-shotcraft shot recipe "Sóng tròn quanh điện thoại và chip" (Remotion composition, Apache-2.0) published on gimgs.net.
Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/radial-ripple-phone-chips.json
Raw TSX: https://gimgs.net/motion/radial-ripple-phone-chips/template.html
Repository: https://github.com/Vincentwei1021/video-shotcraft  (component: demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.tsx; imports demos/_fixtures and assets/lib)
Spec: 1920x1080, 30 fps, 6.1s

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).

JSON · Markdown · llms.txt

4,505 chars · Apache-2.0Mở template gốc ↗
// ===== demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.tsx =====
// radial-ripple-phone-chips — Radial Ripple Chips 同心波纹手机(motion-lab 定稿转原生 Remotion)
// 浅灰底同心圆多层错相呼吸如水波,中央手机 mockup 屏内 feed 缓慢自动滚动,
// 两侧白色 chip 先后 spring pop 入场并悬浮。配色是灰阶骨架 + 单一强调色
//(ACCENT 变量),落地时按项目品牌色替换。
// 设计坐标 480×270(DesignStage 等比放大),参数表数值以此坐标系标定。
import React from 'react';
import { DesignStage, E, lerp, rand, seg, useT } from '../../_fixtures/Motion';

export const RADIAL_RIPPLE_PHONE_CHIPS_DURATION = 168; // 5600ms @30fps

// 模板强调色:实际使用时按项目品牌色替换这一个变量
const ACCENT_RGB = '122,134,153'; // = #7a8699,中性蓝灰做默认演示色
const SANS = '-apple-system,BlinkMacSystemFont,sans-serif';

// 同心圆(深灰→浅灰,从大到小叠放),phase 错相
const RCOL = ['#d7dbe1', '#e0e4e9', '#e8ebef', '#eef0f3'];
const RSZ = [560, 440, 320, 210];
const RINGS = RSZ.map((size, i) => ({ size, color: RCOL[i], phase: i * 1.7 }));

// feed 卡片骨架:渐变图占位 + 两条 rand 定宽的文字条(种子与 effect.js 一致)
const CARDS = Array.from({ length: 8 }, (_, i) => ({
  imgGrad: `linear-gradient(120deg,rgba(${ACCENT_RGB},${(0.5 - i * 0.04).toFixed(2)}),rgba(${ACCENT_RGB},${(0.8 - i * 0.05).toFixed(2)}))`,
  l1w: `${62 + rand(i) * 28}%`,
  l2w: `${34 + rand(i + 40) * 30}%`,
}));

export const RadialRipplePhoneChips: React.FC = () => {
  const t = useT();
  // chip 先后 pop:scale 0.8→1 过冲 + 淡入,随后轻浮动
  const chipStyle = (t0: number, ph: number): React.CSSProperties => {
    const p = seg(t, t0, t0 + 0.12, E.outBack);
    const fl = Math.sin(t * Math.PI * 2 * 2 + ph) * 3 * seg(t, t0 + 0.12, t0 + 0.3);
    return {
      opacity: seg(t, t0, t0 + 0.08),
      transform: `scale(${lerp(p, 0.8, 1)}) translateY(${fl}px)`,
    };
  };
  const chipBase: React.CSSProperties = {
    position: 'absolute',
    padding: '10px 18px',
    borderRadius: 999,
    background: '#fff',
    color: '#2c3038',
    font: `600 13px ${SANS}`,
    whiteSpace: 'nowrap',
    boxShadow: '0 10px 26px rgba(58,64,74,.22)',
  };
  return (
    <DesignStage bg="#f2f3f5">
      <div style={{ position: 'absolute', inset: 0, background: '#f2f3f5', overflow: 'hidden' }}>
        {/* 同心圆各层错相极缓呼吸 */}
        {RINGS.map(({ size, color, phase }, i) => (
          <div
            key={i}
            style={{
              position: 'absolute',
              left: '50%',
              top: '50%',
              width: size,
              height: size,
              margin: `${-size / 2}px 0 0 ${-size / 2}px`,
              borderRadius: '50%',
              background: color,
              transform: `scale(${1 + 0.06 * Math.sin(t * Math.PI * 2 * 1.5 + phase)})`,
            }}
          />
        ))}

        {/* 手机 mockup:屏内 feed 缓慢自动滚动 */}
        <div
          style={{
            position: 'absolute',
            left: '50%',
            top: '50%',
            width: 132,
            height: 264,
            margin: '-132px 0 0 -66px',
            borderRadius: 22,
            background: '#1b1c22',
            padding: 7,
            // 原渲染无全局 border-box:132px 是内容宽,padding 外扩(Remotion 注入
            // 了 * { box-sizing:border-box },这里显式还原 content-box 才对得上原片)
            boxSizing: 'content-box',
            boxShadow: '0 24px 50px rgba(58,64,74,.35)',
          }}
        >
          <div style={{ position: 'relative', width: '100%', height: '100%', borderRadius: 16, background: '#fff', overflow: 'hidden' }}>
            <div
              style={{
                position: 'absolute',
                left: 0,
                top: 0,
                width: '100%',
                transform: `translateY(${-seg(t, 0.08, 0.98) * 150}px)`,
              }}
            >
              {CARDS.map(({ imgGrad, l1w, l2w }, i) => (
                <div key={i} style={{ position: 'relative', margin: '8px 8px 0', padding: 7, borderRadius: 8, background: '#f4f4f7' }}>
                  <div style={{ height: 34, borderRadius: 5, background: imgGrad }} />
                  <div style={{ marginTop: 6, height: 5, width: l1w, borderRadius: 3, background: '#c9cad3' }} />
                  <div style={{ marginTop: 4, height: 5, width: l2w, borderRadius: 3, background: '#dedfe6' }} />
                </div>
              ))}
            </div>
          </div>
        </div>

        {/* 两侧 chip:左先右后 spring pop */}
        <div style={{ ...chipBase, top: '38%', right: 'calc(50% + 86px)', ...chipStyle(0.22, 0) }}>Feature one</div>
        <div style={{ ...chipBase, top: '56%', left: 'calc(50% + 86px)', ...chipStyle(0.4, 1.8) }}>Feature detail</div>
      </div>
    </DesignStage>
  );
};


  1. Read the recipe
    Prompt = when to use, intention, parameter table with typical values and how each one feels, known pitfalls. Treat values as calibrated starting points.
  2. Get the code
    tool: git
    git clone https://github.com/Vincentwei1021/video-shotcraft && cd video-shotcraft && npm install
  3. Register + render
    tool: remotion
    npx remotion render <CompositionId> out.mp4 # component: demos/effects/radial-ripple-phone-chips/RadialRipplePhoneChips.tsx
  4. Or re-implement
    Port the timing/easing from the recipe to HTML + WAAPI (gimgs motion contract) or any engine; keep the same segment windows and easing.
paramtypedefaultdescription
—

Cùng danh mục