地图动效 map-motion
子 skill:「找机位」(照片 → 机位揭秘视频)
photo-spot/;3D 地形与光线推演map-3d/;旅行 vlog(照片 → 卡通小人沿路线走、到站拍照、配乐)travel-vlog/。流程和坑在各自目录;本文件只讲引擎。
一份「镜头表」JSON → 编译(高德解析地名、取路线和边界、算镜头)→ 逐帧渲染(Chromium 画布 + ffmpeg)。
shD=~/.claude/skills/map-motion/scripts $D/make.sh 我的片子.json 成片.mp4 --gif 动图.gif # 一键:编译 + 渲染 $D/make.sh 我的片子.json 静帧/ --stills 1,4.5,9 # 先抽几帧看构图(快)
首次使用:uv run --with playwright playwright install chromium;高德 key 见下。
工作流
- 定内容:问清地点/路线/日期/照片、发哪个平台(定
size)、要什么感觉(定style)。不确定样式时,同一份 spec 换style各渲一帧给用户挑(至少三种)。 - 写 spec:从
assets/examples/挑最近的一份复制改。镜头类型与全部参数见 references/spec.md。 - 编译并核对地点:编译会逐个打印「地点 X → 高德解析结果(级别)[坐标]」。每一行都核对:同名地点会解析错(实测「香格里拉」曾被解析成某家酒店)。不对就换更具体的名字,或直接写坐标。
- 抽静帧:
--stills取每个镜头中段和结尾各一帧。输出末尾有自动质检(出画、文字互压、文字压照片、黑块):「质检:通过」就不用读图;报了问题,或要判断审美(构图、配色)时,才 Read 对应静帧。 - 出片:渲染整片(30fps 约每秒 2.5 帧画面;40 秒片子约 1.5–2 分钟),需要时加
--gif、--audio。长片放后台跑;结束只输出几行摘要(成片路径、时长、边渲边抽检的质检结论),-v才打印逐段进度。中途看进度用python3 scripts/status.py(一行),不要反复读日志。 - 交付:说清时长、文件位置、用了哪些真实数据(高德路线公里数等),以及哪些是示意。
镜头一览
| type | 效果 | 典型用途 |
|---|---|---|
globe | 地球自转 → 俯冲到地点,落点弹标签 | 片头、地点引入 |
locate | 地球俯冲 → 瞄准镜锁定 → 坐标卡(经纬度滚动、海拔、日期) | Vlog 片头「我在这里」 |
pins | 多个地点依次落针+涟漪 | 打卡点、门店 |
route | 高德真实路线或 GPX 描线,车/人沿线走,可镜头跟车、里程表、海拔剖面、终点插旗 | 自驾、徒步登山、骑行、跑步 |
flight | 大圆航线向上拱起,飞机沿线飞 | 出差、旅行、跨城 |
region | 行政区描边 → 填色 → 写名 | 城市群、区域介绍 |
area | 公园/景区/校园轮廓(OSM 边界)描边填色,可让小人绕一圈 | 介绍一个地点、游园路线 |
radiate | 一个中心向多点放射飞线 | 总部/仓库辐射、业务覆盖 |
trip | 多站旅程:逐段路线+累计里程+到站邮戳+照片+原路返程;加 avatar 进入 vlog 模式(卡通形象走路、到站咔嚓拍照、照片弹出、脚步/快门/啵/叮音效踩拍点) | 假期出行复盘、旅行 vlog |
wall | 片尾照片墙:全程照片依次落下 +「N 个机位 · 共走 X km」+ 形象摇摆 | vlog 收尾 |
overview | 镜头框住至今所有地点 | 片尾收束 |
title | 画面中央标题卡 | 开场、章节 |
snap | 地球俯冲 → 机位(相机图标)+被摄地 → 取景扇形 → 取景框对焦 → 咔嚓(自动配快门声)→ 照片从机位翻折立起,背景是这张照片的虚化;live 可做 iPhone Live Photo 播放效果 | 「这张照片在哪拍的」、机位揭秘、摄影 Vlog |
guess | 整屏照片+「这张照片在哪拍的?」+提示+倒数,闪白露出地球,接 locate 揭晓 | 猜地点系列、悬念片头 |
3D 地形模式("mode": "3d")
真立体山体(AWS Terrain Tiles 高程 + Esri 卫星,MapLibre GL 渲染)。整片是固定一套:卫星地球俯冲 → 整条轨迹 3D 鸟瞰(白线 + 标题)→ 压低贴到起点 → 跟着人走(黄线、里程、海拔、累计爬升、小地图、底部海拔剖面;到照片点停下弹照片)→ 拉回全景、出总数据。写法见 references/spec.md;流程、例子和检查命令在子 skill map-3d/。
- 坐标一律 WGS-84(GPX、OSM、卫星、高程都是),所以国内也用 Esri 卫星;高德坐标写
"gcj": true。 - 没有 GPX:
"track": {"via": [途经点…]}沿 OSM 小路取最短路连起来(山里 OSM 常断头,不连通就加中间点)。 - 软件 WebGL(SwiftShader)渲染,约 1–1.5 秒一帧,30 秒片子 15–25 分钟,放后台跑;先
--stills抽帧看。
镜头之间自动「飞过去」(van Wijk 平滑缩放:远距离先拉远再推近,和 Mapbox flyTo 同一公式),不需要手写转场。
样式
| style | 底图 | 适合 | 最大推近 |
|---|---|---|---|
amap | 高德标准路网瓦片 | 通用、街道级路线 | 17.5 |
amap-dark | 高德瓦片反色 | 夜景感、科技向 | 17.5 |
amap-gray | 高德瓦片去色,红色路线 | 数据可视化、新闻图解 | 17.5 |
amap-sepia | 高德瓦片棕褐调,手写字 | 怀旧、城市故事 | 17.5 |
amap-journal | 高德瓦片淡暖色 + 纸纹 + 手写字,红色虚线路线 | 旅行 vlog、citywalk 手账 | 17.5 |
satellite | 高德卫星+路名,地球也是真实卫星图 | 片头俯冲、户外、航线 | 17.5 |
satellite-world | Esri 全球卫星(无路名),署名 Esri | 境外地点(高德卫星境外只有城市级,再推近是灰底) | 17.5 |
dark | 矢量:深底发光边界与飞线 | 数据/业务分布 | 8 |
light | 矢量:白底极简 | 区域介绍、信息图 | 8 |
journal | 矢量:纸纹水彩省份、红虚线、邮戳、手写字 | 旅行手账 | 8 |
vintage | 矢量:复古牛皮纸、棕墨线 | 历史、怀旧 | 8 |
neon | 矢量:深紫底、品红海岸线、青色发光飞线 | 潮流、夜生活、音乐 | 8 |
ink | 矢量:宣纸底、水墨陆地、朱砂路线与印章 | 国风、文旅、历史路线 | 8 |
blueprint | 矢量:蓝底白线网格、虚线路线 | 工程、规划、科普 | 8 |
矢量样式的边界是抽稀过的(约 2 km 精度),编译时自动把镜头封顶在 8 级(约一个地级市范围)并提示;要街道细节就用前三种。
高德
- key:
export AMAP_KEY=…或写入~/.config/map-motion/amap_key(Web服务类型)。没 key 时编译直接报错并说明怎么申请。不要把 key 写进 spec 或项目目录。 - API 响应缓存在
~/.cache/map-motion/api/,瓦片缓存在~/.cache/map-motion/tiles/;改 spec 重编译不重复消耗配额。 - 用户的 GPS/照片 EXIF/GPX 坐标是 WGS-84,spec 里写
"wgs84": true转成高德坐标。 - 海拔:
elevation: true用 OpenTopoData SRTM 30m(免费,每天约 1000 次请求,已缓存);查之前把高德坐标换回 WGS-84,否则山区高度会错。 - 高德瓦片属非官方取用:个人/学习视频可用并保留右下角署名;商业投放需高德授权或改矢量样式。国界、南海九段线照内置数据画,不要删改。详见 references/amap.md。
要改画法时
- 画面全在
scripts/runtime/mm.js:样式表ST(颜色、字体、是否发光/纸纹/虚线)、各图层绘制函数(routepinregionhudTitleodometercaptionphoto)、地球(globeTexture/globeFill逐像素反投影)。加样式=往ST加一项(地球颜色用globeOcean/globeLand单独给,暗色样式要拉开对比),用瓦片的再加进compile.py的TILED。 - 镜头逻辑在
scripts/compile.py的shot_*函数;加镜头类型=写一个shot_xxx并登记到SHOTS。 - 改完用
assets/examples/showcase.json(覆盖全部镜头)在两三种样式下抽帧回归。
踩过的坑(改代码前先看)
-
3D:MapLibre 5.6 在地球↔地形切换时抛
clearFadeHold错 → 用 5.24(render.py的VENDOR)。setData在 worker 里异步,idle可能先到,线会慢一帧 →t3d.js先等sourcedata(isSourceLoaded)再等idle。 -
3D:单帧抽图看着没问题、连续出片却在远处露黑块(天空/远处地形没画)——抽帧检查要用
--from/--to渲一小段,别只看--stills。现在的处理:推近到 12.2 级后从地球投影换成平面投影(地球投影下雾和天空不生效;12 级前换会跳一下),底色透明、天空由 2D 画布自己画渐变,全景/片尾俯角压到 40–44° 不露地平线。 -
3D:雪山上白字看不清 → HUD 上下压暗渐变。俯视全景按透视近大远小会出画 → 自动挑让轨迹顺着视线方向的朝向,再退 0.35 级。
-
正射地球上用多边形填陆地会破(背面点贴地平圈后围出错误区域,整球变白)→ 现在逐像素反投影取纹理;海岸线、省界只描线。
-
墨卡托下逐点做 ±180° 归位,环会在屏幕两侧来回跳、画出横贯全屏的线 →
Pline只对首点归位,之后按经度差连续累加。 -
推得很近时顶点投影到几十万像素外,画布填充破成楔形 →
clipRing先按屏幕外扩框裁剪。 -
地名先做 POI 搜索会把行政区名搜成同名商户;只信地理编码又会把「青海湖」编成乌鲁木齐同名小区 →
geocode先编码,落到兴趣点/道路/公交站/住宅区级才退 POI。编译打印的地点行仍要逐行核对。 -
过渡阶段的透明度要从外层一路传进
drawGlobe,内部重设globalAlpha=1会让交叉淡化失效。 -
从行政区推到街道级,区域填色会染满整屏 → 推近前那一镜写
clear(编译主循环统一处理)。 -
OSM 主站常 504、单个镜像会超时 →
osm.py轮流试三个镜像并缓存;按名字查会查到同名地铁站,已按 railway/public_transport 标签排除。


