相机与动画
MapLibre 的相机有六个自由度:中心点、缩放、旋转、倾斜。切换相机有四个方法, 动画由内部的缓动函数驱动,不是 CSS 过渡,因此不能用 CSS 的 duration 或 easing 干预。
四个方法
| 方法 | 动画 | 适用 |
|---|---|---|
jumpTo | 无,立即生效 | 初始化、恢复书签视角 |
easeTo | 缓动,可选线性 | 短距离平移、俯仰调整 |
flyTo | 先拉远再拉近的抛物线 | 跨区域跳转,视觉上有「飞过去」的感觉 |
fitBounds | 内部调用 easeTo | 让某个范围刚好装满容器 |
// 立即定位
map.jumpTo({ center: [116.391263, 39.907359], zoom: 11 });
// 缓动平移,保持缩放
map.easeTo({
center: [116.407000, 39.899000],
duration: 800,
easing: function (t) { return t * (2 - t); }
});
// 跨城市飞行
map.flyTo({
center: [121.473701, 31.230416],
zoom: 12,
bearing: 0,
pitch: 35,
duration: 3600,
curve: 1.4, // 路径弯曲程度,越大越「高飞」
essential: true // 用户开启减少动效时仍然执行
});
essential: true 值得注意:系统启用「减少动态效果」时,
MapLibre 会把 flyTo 降级为直接跳转。业务上必须让用户看到位置变化时,
显式标记为 essential。
适配范围
fitBounds 接受一个 [[西, 南], [东, 北]] 的数组,
或者 LngLatBounds 实例。四个参数决定最终视野:
| 参数 | 作用 | 默认 |
|---|---|---|
padding | 四边留白,像素或 {top, bottom, left, right} | 0 |
maxZoom | 结果缩放的上限 | 无 |
duration | 动画时长,毫秒 | 500 |
linear | 是否匀速,关闭则用缓动 | false |
offset | 整体平移结果,给侧栏让位 | [0, 0] |
var bounds = [
[116.20, 39.80], // 西南
[116.55, 40.05] // 东北
];
map.fitBounds(bounds, {
padding: { top: 40, bottom: 120, left: 320, right: 40 },
maxZoom: 15,
duration: 1200
});
左侧有固定侧栏时,padding.left 必须等于侧栏宽度,
否则范围虽然装下了,但被侧栏遮掉一部分。用 padding
而不是事后 panBy,结果更稳定。
动画时序与取消
动画期间 map.getCenter() 返回的是当前帧的位置,不是终点。
需要终点数据时,从事件或传入的参数里取。动画被打断时
moveend 仍会触发一次。
function flyHome() {
map.once('moveend', function () {
console.log('到达', map.getCenter().toArray(), map.getZoom());
});
map.flyTo({ center: [116.391263, 39.907359], zoom: 12 });
}
// 用户开始手动操作时,取消正在进行的动画
map.on('dragstart', function () {
map.stop();
});
map.stop() 立即结束动画并停在中途位置,不返回起点。
需要「取消后回到原处」就得自己记下起始状态,再 easeTo 回去。
连续动画与帧驱动
相机动画也可以用 easeTo 做帧驱动:每帧算一次新中心,
关掉动画时长,得到匀速旋转或跟随效果。
var bearing = map.getBearing();
function spin() {
bearing = (bearing + 0.3) % 360;
map.easeTo({
bearing: bearing,
duration: 0, // 立即生效,由下一帧驱动
animate: false
});
requestAnimationFrame(spin);
}
// 开始前先停掉用户交互,避免视角被抢
map.dragRotate.disable();
spin();
duration 与 animate 是两套开关。animate: false
直接禁用动画;只把 duration 设成 0 时,部分版本仍会走一帧的补间。
需要绝对即时,两个都写。
注意事项
fitBounds 遇到跨 180 度经线
从东经 170 度到西经 170 度的范围,直接写成
[[170, ...], [-170, ...]] 会让相机横穿整个地球。
这种跨日期变更线的范围,需要拆成两块分别适配,或先把经度统一到连续区间。
动画期间修改样式
setStyle 会重建渲染管线,正在进行的相机动画被打断,
moveend 可能与 styledata 交错触发。
换底图与飞行动画不要同时进行,先 map.stop()。
移动端长动画耗电
三秒以上的 flyTo 会让 GPU 持续满负荷。移动端把
duration 压到 1500 毫秒以内,或改用 easeTo,
体感差异不大,耗电与发热明显更低。
经纬志(2026)。《相机与动画》。https://www.jingweizhi.com/maplibre/tutorial/camera-animation.html(访问日期 2026-11-03)。