Motion · Camera

Chuyển động máy quay đa lớp

Hai kiểu chuyển động máy quay theo chiều sâu phân lớp: dolly thị sai đa lớp (3 lớp di chuyển ngang theo dải tốc độ tạo chiều sâu) và dolly-zoom giả (chủ thể đứng yên, nền phình to áp vào).

1920×108016:930 fps4.54s · 136 frameremotionGiấy phép: Apache-2.0#shotcraft#remotion#camera

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

Biến thể

multiplanemultiplane · Dolly thị sai đa lớp
Dolly thị sai đa lớp — 3 lớp chất liệu di chuyển ngang theo dải tốc độ (gần nhanh, xa chậm), ảnh chụp màn hình phẳng trượt ra thành chiều sâu.
dolly-zoomdolly-zoom · Dolly-zoom giả
Dolly-zoom giả — chủ thể đứng yên bất động trong khi lớp nền liên tục phình to áp vào, tạo áp lực dồn nén cho khoảnh khắc kịch tính.

Khi nào dùng

Dùng cho các đoạn cần ảnh chụp màn hình phẳng có "độ dày"; dùng dolly-zoom cho khoảnh khắc kịch tính cần dồn nén (≤1 lần/video).

Ý đồ

Lia toàn trang là "nhìn trang", còn chiều sâu phân lớp là "ở trong không gian của trang". Dolly thị sai bổ sung chiều sâu cho chuyển động ngang (nguyên lý máy quay đa mặt phẳng của Disney, cùng chất với phim Linear); dolly-zoom giả thì ngược lại — nhân vật chính đứng yên tuyệt đối trong khi cả thế giới áp vào, dồn nén cho khoảnh khắc kịch tính.

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

  • Thời lượng: Dolly thị sai: kéo dài 4–5s; dolly-zoom: hành trình một chiều 3–4s.
  • Năng lượng: Thị sai = Trung bình (thiên về chất liệu); dolly-zoom = Trung bình cao (áp lực tăng dần)

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)

意图

整页平移是"看页面",分层深度是"在页面的空间里"。视差滑轨给横移 加纵深(迪士尼多平面摄影机原理,Linear 片同款质感);伪 dolly-zoom 反过来——主角纹丝不动、全世界压过来,给戏剧性时刻蓄力。

动效核心

  • 视差滑轨(3 层):背景整页(系数 0.35 + blur 2px + 降饱和

0.92 + opacity 0.85,退成环境)、中景真实卡切片(系数 0.7, 主阅读层,无 blur)、前景浮块(系数 1.4 + blur 3px,掠过镜头)。 同一条 drive 位移乘各层系数

  • 伪 dolly-zoom:主体高清卡固定 px 钉屏中(不参与任何变换);

背景层(整页 + 卡群一体)scale 1→2.25 从画面中心膨胀 + blur 0→3.5px 渐深;主体落影随进程加深(12→28px offset)强化"钉住感"

  • 两款都不拆真实页面排版:整页作为一体成层,独立元素用切片另起层

——层间关系是"景深",不是"页面被撕开"

参数表

| 参数 | 典型值 | 调节手感 | |------|--------|----------| | 视差层系数 | 0.35 / 0.7 / 1.4 | 梯度 ≥2 倍层间才可辨;层数 ≤4 防过载 | | 层次锚 | 前后景 blur + 背景降饱和 | 没有 blur/饱和锚,读作"贴片乱飞" | | dolly 膨胀行程 | scale 1→2.0–2.5 | 行程小了压迫感不足;背景 opacity ≤0.6 防读作故障 |

已知坑

  • 主阅读层/主体必须高清纹理且无 blur——观众在读它
  • 视差与 PageCam rot 3D 的组合(斜拍视差)未验证,实战先渲 still 确认
  • dolly-zoom 是"戏剧调料":一支片 ≤1 次,日常段落用视差滑轨
  • 参数经占位素材调校转正,非实战定稿,首次实战后回验

参考实现

demos/camera/depth-layer-moves/ (DollyZoomReal.tsx / MultiplaneReal.tsx)

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

  • demos/camera/depth-layer-moves/DollyZoomReal.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/camera/depth-layer-moves/DollyZoomReal.tsx
  • demos/camera/depth-layer-moves/MultiplaneReal.tsx — https://raw.githubusercontent.com/Vincentwei1021/video-shotcraft/main/demos/camera/depth-layer-moves/MultiplaneReal.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/depth-layer-moves.json
git clone https://github.com/Vincentwei1021/video-shotcraft
# component: demos/camera/depth-layer-moves/DollyZoomReal.tsx, demos/camera/depth-layer-moves/MultiplaneReal.tsx
npx remotion render <CompositionId> out.mp4
claude mcp add --transport http gimgs https://gimgs.net/mcp
# then: search_motion_templates / get_motion_template("depth-layer-moves")

Prompt

Use the video-shotcraft shot recipe "Chuyển động máy quay đa lớp" (Remotion composition, Apache-2.0) published on gimgs.net.
Recipe JSON (prompt = timing/easing/parameter table/pitfalls, files = TSX source): https://gimgs.net/motion/depth-layer-moves.json
Raw TSX: https://gimgs.net/motion/depth-layer-moves/template.html
Repository: https://github.com/Vincentwei1021/video-shotcraft  (component: demos/camera/depth-layer-moves/DollyZoomReal.tsx, demos/camera/depth-layer-moves/MultiplaneReal.tsx; imports demos/_fixtures and assets/lib)
Spec: 1920x1080, 30 fps, 4.54s

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,387 chars · Apache-2.0Mở template gốc ↗
// ===== demos/camera/depth-layer-moves/DollyZoomReal.tsx =====
// dolly-zoom 滑动变焦(轮 F)——主体卡(card4-hires)大小锁定屏中,
// 背景真实卡群 + 整页反向膨胀逼近(scale + blur 渐深),
// "世界压过来"而主角纹丝不动。伪 dolly-zoom:无需 3D,分层反向补偿。
import { AbsoluteFill, Img, interpolate, staticFile, useCurrentFrame, Easing } from 'remotion';
import layout from '../../_textures/live-layout.json';

export const DOLLYZOOM_DUR = 135;

const BG_CARDS = layout.projects.cards.filter((_, i) => i !== 3);

export const DollyZoomReal: React.FC = () => {
  const frame = useCurrentFrame();
  const t = interpolate(frame, [15, 110], [0, 1], {
    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
    easing: Easing.bezier(0.4, 0, 0.3, 1),
  });
  const bgScale = 1 + t * 1.25;
  const bgBlur = t * 3.5;
  return (
    <AbsoluteFill style={{ backgroundColor: '#efece6', overflow: 'hidden' }}>
      {/* 背景:整页 + 卡群,从画面中心膨胀逼近 */}
      <div
        style={{
          position: 'absolute', inset: 0,
          transform: `scale(${bgScale})`, transformOrigin: '960px 540px',
          filter: `blur(${bgBlur}px) saturate(0.9)`,
        }}
      >
        <Img src={staticFile('textures/live/projects-full.png')} style={{ position: 'absolute', left: 0, top: -400, width: 1920, opacity: 0.55 }} />
        {BG_CARDS.slice(0, 8).map((c, k) => (
          <Img
            key={c.file + k}
            src={staticFile(`textures/live/${c.file}`)}
            style={{
              position: 'absolute',
              left: [180, 1280, 240, 1220, 700, 760, 60, 1500][k],
              top: [140, 120, 700, 720, 60, 840, 420, 430][k],
              width: 320, borderRadius: 12, opacity: 0.9,
              boxShadow: '0 6px 20px rgba(31,28,23,0.12)',
            }}
          />
        ))}
      </div>
      {/* 主体:高清卡视觉大小恒定钉在屏中,落影渐深强调"世界在动我不动" */}
      <Img
        src={staticFile('textures/live/card4-hires.png')}
        style={{
          position: 'absolute', left: 960 - 260, top: 540 - 228, width: 520,
          borderRadius: 14,
          boxShadow: `0 ${12 + t * 16}px ${40 + t * 28}px rgba(31,28,23,${0.16 + t * 0.10})`,
        }}
      />
    </AbsoluteFill>
  );
};


// ===== demos/camera/depth-layer-moves/MultiplaneReal.tsx =====
// multiplane parallax(轮 E)——真实页面拆 3 层深度横移:
// 背景整页(0.35x,微 blur 退后)+ 中景真实卡组(0.7x)+
// 前景浮块(1.4x,search 切片 + 高清卡,轻 blur 拉焦平面)。
// 系数克制(背景不动排版,卡组独立成层)防"排版散架"。
import { AbsoluteFill, Img, interpolate, staticFile, useCurrentFrame, Easing } from 'remotion';
import layout from '../../_textures/live-layout.json';

export const MULTIPLANE_DUR = 135;

const CARDS = layout.projects.cards.slice(0, 6);

export const MultiplaneReal: React.FC = () => {
  const frame = useCurrentFrame();
  const drive = interpolate(frame, [10, 125], [0, 1000], {
    extrapolateLeft: 'clamp', extrapolateRight: 'clamp',
    easing: Easing.bezier(0.35, 0, 0.25, 1),
  });
  return (
    <AbsoluteFill style={{ backgroundColor: '#efece6', overflow: 'hidden' }}>
      {/* 背景层 0.35x:整页微退焦 */}
      <div style={{ position: 'absolute', inset: 0, transform: `translateX(${-drive * 0.35}px) scale(1.05)`, filter: 'blur(2px) saturate(0.92)', opacity: 0.85 }}>
        <Img src={staticFile('textures/live/projects-full.png')} style={{ position: 'absolute', left: 0, top: -300, width: 2100 }} />
      </div>
      {/* 中景层 0.7x:真实卡组横排(主阅读层) */}
      <div style={{ position: 'absolute', top: 330, transform: `translateX(${-drive * 0.7}px)` }}>
        {CARDS.map((c, k) => (
          <Img
            key={c.file}
            src={staticFile(`textures/live/${c.file}`)}
            style={{
              position: 'absolute', left: 200 + k * 480, top: (k % 2) * 40,
              width: 420, borderRadius: 12,
              boxShadow: '0 8px 28px rgba(31,28,23,0.14)',
            }}
          />
        ))}
      </div>
      {/* 前景层 1.4x:浮块掠过(search 切片 + 高清卡),轻 blur */}
      <div style={{ position: 'absolute', inset: 0, transform: `translateX(${-drive * 1.4}px)`, filter: 'blur(3px)' }}>
        <Img src={staticFile('textures/live/float-search.png')} style={{ position: 'absolute', left: 900, top: 150, width: 700, borderRadius: 22, boxShadow: '0 12px 36px rgba(31,28,23,0.18)' }} />
        <Img src={staticFile('textures/live/card4-hires.png')} style={{ position: 'absolute', left: 2100, top: 700, width: 380, borderRadius: 12, boxShadow: '0 12px 36px rgba(31,28,23,0.20)' }} />
      </div>
    </AbsoluteFill>
  );
};


  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/camera/depth-layer-moves/DollyZoomReal.tsx, demos/camera/depth-layer-moves/MultiplaneReal.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