mcpField
M
map-animation

map-animation

github.com/iart-ai创意与媒体21824 次安装18 周前更新
代码仓库

关于 map-animation

map-animation 是为视频创作者和数据驱动解说内容提供的编辑风格地图动画技能。它结合 Google Earth Studio 与 After Effects 的镜头运动、GeoJSON/SVG 地图叠加元素,以及 12fps 图形动画节奏。

主要功能

  1. 01用 Google Earth Studio 制作镜头运动
  2. 02基于 GeoJSON 或 SVG 构建矢量地图
  3. 03动画呈现标记、路线、区域和标签
  4. 04将图形动画量化为 12fps
  5. 05通过坐标数组驱动动画序列

适用场景

  1. 01在纪录片中交代地点
  2. 02呈现路线、迁徙或供应链
  3. 03突出并标注地理区域
  4. 04根据事件数据重复渲染地图视频

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/iart-ai/map-animation-skills --skill map-animation

安装后在 Claude Code 中输入/map-animation确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
references/
earth-studio-to-ae.md
vector-maps.md

详细介绍

地图动画

制作具有编辑感的“Vox 风格”动画地图:镜头在底图上缩放、环绕和平移,同时地点标记落下、路线逐渐绘出、区域高亮显示、标签弹出——所有动画都以精心设定的 12fps 卡顿节奏同步,让运动呈现出图形设计感,而非摄影感。有两条制作路径:使用 Google Earth Studio 制作逼真的底图运动,再在 After Effects 中合成;或使用 GeoJSON/SVG 矢量地图,通过 Remotion 或 D3 根据坐标数组驱动渲染。

适用场景

  • 用于纪录片或解说视频中交代地点:从地球缩放到城市、环绕地标,或沿边界平移。
  • 展示地理范围内的移动:两点之间逐渐绘出的路线、供应链、迁徙路线或飞行路径。
  • 高亮并标注某个国家、州或地区;制作逐步展开的地理导览。
  • 将坐标或事件列表转为可重复、数据驱动的视频地图片段。

选择路径

需求路径输出
写实地形、真实卫星影像/3D 建筑、电影感镜头Google Earth Studio → After Effects图像序列 + 摄像机 .jsx,在 AE 中合成
简洁的编辑风格矢量画面、完全可控、数据驱动Remotion 或 D3 中的 矢量地图(GeoJSON/SVG)代码渲染的 MP4/GIF 或网页 SVG
在任一路径上叠加标记/路线/高亮/标签叠加层AE 形状/空对象图层,或 React/SVG 组件

大多数“Vox 风格”作品会将两者结合:用 Earth Studio 制作底图镜头运动,再在上方叠加矢量标记、路线和标签。

标志性风格:12fps 卡顿

这种风格最容易辨认的特征,就是在 24fps 的剪辑中以 12fps 制作地图图形动画。底片和音频以 24fps 播放(流畅);图形层——由镜头驱动的标记、逐渐绘出的路线、弹出的标签——则以 12fps 逐帧跳动。这样呈现出的运动更像刻意制作的手工“信息图”,而非柔和的视频画面。

  • **计算方式:**在 24fps 下,每个图形层数值保持 2 帧(仅在偶数帧更新)。在 30fps 下,保持约 2–3 帧(≈12.5/10fps)。
  • **After Effects:**对图层/合成应用 posterizeTime(12),或将图形放入 12fps 预合成,再嵌入 24fps 主合成。
  • **Remotion/代码:**在计算任何动画数值前,将帧数对齐至指定节奏:
// Quantize the timeline to 12fps "steps" while the comp renders at 24fps.
const STEP = Math.round(fps / 12);              // 2 at 24fps
const f = Math.floor(frame / STEP) * STEP;       // use `f`, not `frame`, for graphic motion
const progress = interpolate(f, [0, 48], [0, 1], { extrapolateRight: "clamp" });

保持底片/镜头平滑(24fps),只让图形叠加层卡顿——所有画面都卡顿看起来像丢帧,而不是风格设计。

路径 A — Google Earth Studio → After Effects

Earth Studio 是一款浏览器工具,可为 Google 的 3D 地球设置镜头关键帧,并导出两个可导入 AE 的文件:图像序列(渲染帧)以及摄像机轨迹 .jsx 脚本,用于在 After Effects 中重建完全一致的 3D 摄像机。

  1. 在 Earth Studio 中设置关键帧(浏览器):为摄像机位置(纬度/经度/高度)、倾斜、方位角和视场角设置关键帧。使用自动缓动,或为每个关键帧设置自定义缓动。镜头运动要缓慢、沉稳——用于交代场景,不要突然甩动。
  2. 导出 → 渲染:选择图像序列(JPG/PNG),设置分辨率和帧率,并勾选“3D Camera Export”/After Effects 复选框,以便在导出帧的同时生成 .jsx 和轨迹点文件。
  3. **在 After Effects 中:将图像序列作为素材导入,然后选择File → Scripts → Run Script File…**并运行导出的 .jsx。它会创建一个包含 3D 摄像机和空对象图层的合成,其关键帧与 Earth Studio 的镜头运动逐帧匹配。
  4. **将叠加层关联到轨迹:**把标记/标签空对象图层关联到导入的 3D 摄像机/轨迹空对象,这样摄像机移动时,它们会固定在正确的地图位置上(.jsx 包含你在 Earth Studio 中标记的坐标所对应的轨迹点)。
  5. 仅对叠加层预合成应用卡顿效果(posterizeTime(12)),为底片调色,然后渲染。

请参阅 references/earth-studio-to-ae.md,了解具体的关键帧/导出设置、.jsx 导入步骤,以及轨迹点如何将屏幕空间映射到纬度/经度。

路径 B — 矢量地图(GeoJSON / SVG)

要呈现简洁的编辑风格,可将 GeoJSON 投影为 SVG 路径并为其添加动画。使用真实投影,确保坐标位置准确。

import { geoMercator, geoPath } from "d3-geo";
const projection = geoMercator().fitSize([1920, 1080], geojson);  // fit map to frame
const toPath = geoPath(projection);
const [x, y] = projection([lng, lat]);          // any lat/long → screen pixel

将 geojson.features 渲染为 <path d={toPath(f)} />。同一个 projection([lng,lat]) 会将每个定位点坐标转换为屏幕位置,因此当镜头(scale/translate 变换)移动时,定位点、路线和标签都使用同一坐标空间。

矢量地图中的镜头移动是通过 CSS/变换,朝目标经纬度缩放和平移:

const [tx, ty] = projection([targetLng, targetLat]);
const scale = interpolate(f, [0, 36], [1, 4], { extrapolateRight: "clamp" });
// transform-origin at the target so the zoom homes in on it
const transform = `translate(${960 - tx*scale}px, ${540 - ty*scale}px) scale(${scale})`;

叠加层:定位点、路线、高亮、标签

定位点落下(落下、稳定,可选脉冲)——按坐标数组依次错开显示:

const pins = [{ name:"Kyiv", lng:30.52, lat:50.45 }, { name:"Lviv", lng:24.03, lat:49.84 }];
pins.map((p, i) => {
  const local = f - i * STEP * 4;                 // stagger, quantized to the 12fps step
  const drop = spring({ frame: local, fps, config:{ damping: 12 } });   // overshoot = settle
  const [x, y] = projection([p.lng, p.lat]);
  return <Pin key={p.name} x={x} y={y - 40*(1-drop)} opacity={local > 0 ? 1 : 0} />;
});

路线绘制——使用投影构建路径,再通过 stroke-dashoffset 绘制描边:

// route = [[lng,lat], ...]; project each point, join into an SVG path
const d = route.map((c,i) => (i ? "L" : "M") + projection(c).join(" ")).join(" ");
// in the component: pathLength=1, strokeDashoffset = 1 - drawProgress
const draw = interpolate(f, [12, 60], [0, 1], { extrapolateLeft:"clamp", extrapolateRight:"clamp" });

pathLength="1" 会将任意路线长度归一化,因此一个进度值(0→1)就能绘制路线。添加一个沿 offset-distance 移动的圆点,引领线条前进。若要绘制大圆弧线或弯曲的飞行路径,可使用 d3.geoInterpolate(a, b) 采样中间点。

区域高亮——填充/描边匹配的 GeoJSON 要素,并将其余部分变暗(突出重点,保留上下文):

const isTarget = (f) => f.properties.name === "France";
// target: fill accent, opacity 1, stroke up; others: opacity .35

可通过插值填充不透明度来淡入高亮,也可使用剪切路径蒙版擦除显示。颜色应始终代表固定含义,切勿在同一作品中途重新定义。

标签标注——定位点标签或引线框;待定位点落定后再淡入/滑入,并锚定到 projection([lng,lat]),使其随镜头移动。保持标签水平且位于安全区域内;切勿让标签随地图旋转。

数据驱动的编排

通过一个有序数组驱动整个序列,让地图自行讲述,并根据数据重新渲染:

const beats = [
  { lng: 30.52, lat: 50.45, label: "Kyiv",  hold: 48 },
  { lng: 24.03, lat: 49.84, label: "Lviv",  hold: 36, route: true },
];
// Each beat = a Sequence: camera flies to (lng,lat), pin drops, label pops, route draws if set.
// Total duration = sum(holds); compute it in calculateMetadata, not by hand.

一个数组即可让镜头目标、定位点顺序、路线分段和标签保持同步。替换数据即可获得新的地图序列,无需重新设置关键帧。

输出检查清单

  • 镜头移动缓慢且有意图(逐步建立画面,不要猛移);每个关键帧都使用缓动。
  • 图形叠加层以 12fps 播放;基础素材/镜头保持流畅(24fps)。
  • 镜头移动时,定位点/路线/标签始终锁定在各自的经纬度位置(附着到轨迹 / 共用投影)。
  • 按阅读顺序绘制路线;高亮采用突出重点并保留上下文的方式(目标区域点亮,其余部分变暗)。
  • 标签保持水平、清晰可读且位于安全区域内;颜色规则保持一致。
  • 数据驱动路径:使用一个坐标/节拍数组作为唯一事实来源。

交付与验证(渲染静帧 → MP4)

打包的辅助脚本(scripts/):使用 scripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.png 将静帧拼成图集,然后使用 scripts/probe-mp4.sh out.mp4 [WxH] [fps] 验证编码结果。请参阅 scripts/README.md。

可通过代码渲染的方案是 Remotion 合成——逐帧确定性渲染,因此任何指定帧都可以无头渲染,无需寻帧工具。当交付物为 MP4/GIF(矢量地图、数据驱动、标签精确)或按数据集批量处理时,使用此方案;对于 Earth Studio + AE 合成,交付物则是 AE 项目和渲染的素材,通过逐帧检查主合成进行验证。

输出约定:

  • 一个已注册合成的 Remotion 项目(<Composition> + zod schema + defaultProps),所有动画均由帧驱动(不使用计时器 / Date.now() / Math.random())。
  • 交付物 = 渲染后的 out/*.mp4(以及项目和 beats/坐标数组,以便使用新数据重新渲染地图)。
  • 12fps 卡顿效果根据 fps 计算(量化帧数),而不是硬编码——因此调整 fps 后仍然有效。
  • 时长取决于数据(各拍停留时长的总和)?请在 calculateMetadata 中计算,而不是手动填写。

**验证流程——渲染静帧 → 检查 → 编码。**先渲染单帧(开销低,无需编码)并检查;确认帧正确后再进行编码。

# Frame-exact stills at start / each beat / end — render with the SHIPPED props (the real beats array)
npx remotion still MapSeq out/f-start.png --frame=0   --props='{...}'
npx remotion still MapSeq out/f-beat2.png --frame=N   --props='{...}'   # mid a beat's hold
npx remotion still MapSeq out/f-end.png   --frame=L   --props='{...}'   # L = durationInFrames - 1

# Inspect each PNG — FIDELITY: every pin sits on the correct lat/long, the route connects the right
# cities, the highlighted region is the intended one, labels are spelled right and readable. And land
# on TWO adjacent frames inside a move to confirm the overlay holds for 2 frames (the 12fps step),
# while the base camera is still advancing every frame.
# ARTIFACTS: labels off-canvas/clipped, pins drifting off their coordinate, route overshooting,
# missing map tiles/fonts, projection cutoff at the antimeridian.

# Only after the stills check out, encode:
npx remotion render MapSeq out/map.mp4 --props='{...}'
  • npx remotion compositions 会读取 durationInFrames/fps,以确定结束帧和每拍的采样帧。
  • **数据驱动 / 批量处理:**先通过静帧验证一个代表性的 beats 数组,再批量渲染所有数据集——一次发现投影/标签问题,而不是重复发现 N 次。
  • 免费生成 README 演示 GIF:npx remotion render MapSeq out/demo.gif --codec=gif。

完成前:

  1. npx remotion still 在第 0 帧、每拍中间帧和最后一帧均能干净地完成渲染——无错误,不缺少瓦片/字体。
  2. 每个标记/路线/标签在所有已检查帧中都落在正确的投影坐标上(抽查一座已知城市)。
  3. 两个相邻的叠加层帧完全相同(12fps 步进保持),同时底层摄像机已移动——卡顿效果确实存在,且仅作用于图形层。
  4. 仅由帧驱动——不使用 Date.now() / Math.random() / 计时器;随项目交付的 beats 能正确渲染(而不仅仅是 defaultProps)。
  5. 已编码完整 MP4,且可正常播放;(可选)为 README 渲染 GIF。

参考文件

  • references/earth-studio-to-ae.md — Google Earth Studio 关键帧与导出设置、After Effects .jsx 摄像机导入工作流、将叠加层关联到跟踪点、纬度/经度 ↔ 屏幕空间映射,以及将 12fps 卡顿效果应用到叠加层预合成。
  • references/vector-maps.md — GeoJSON 投影设置、将矢量摄像机缩放/平移到纬度/经度、标记落下 / 路线绘制 / 区域高亮 / 标签标注方案、数据驱动的 beats 模式,以及适用于代码和 AE 的完整 12fps 卡顿量化方案。

评论

支持 Markdown