PosAnimation位置动画介绍
# 概述
PosAnimation
是 Leaflet 中实现元素位置动画的核心模块,用于平滑地将元素从一个位置过渡到另一个位置,支持自定义持续实际和缓动效果。
# 源码分析
# 源码实现
PosAnimation
源码实现如下:
export var PosAnimation = Evented.extend({
run: function (el, newPos, duration, easeLinearity) {
this.stop(); //停止正在进行的动画
this._el = el; // 目标元素
this._inProgress = true; //标记动画进行中
this._duration = duration || 0.25; //动画时长(默认0.25秒)
this._easeOutPower = 1 / Math.max(easeLinearity || 0.5, 0.2); //缓动强度(防止除零)
this._startPos = DomUtil.getPosition(el); // 初始位置
this._offset = newPos.subtract(this._startPos); // 总位移向量
this._startTime = +new Date(); //动画开始时间戳
this.fire("start"); //触发开始事件
this._animate(); // 启动动画循环
},
stop: function () {
if (!this._inProgress) {
return;
}
this._step(true); // 强制跳转到最终位置
this._complete(); // 完成动画
},
_animate: function () {
this._animId = Util.requestAnimFrame(this._animate, this); //请求下一帧
this._step(); //更新位置
},
_step: function (round) {
var elapsed = +new Date() - this._startTime,
duration = this._duration * 1000; //转换为毫秒
if (elapsed < duration) {
this._runFrame(this._easeOut(elapsed / duration), round); // 更新当前帧
} else {
this._runFrame(1); // 强制到达最终位置
this._complete(); //结束动画
}
},
_runFrame: function (progress, round) {
var pos = this._startPos.add(this._offset.multiplyBy(progress)); //计算新位置
if (round) {
pos._round(); //四舍五入避免亚像素
}
DomUtil.setPosition(this._el, pos); //应用新位置
this.fire("step"); // 触发每帧更新事件
},
_complete: function () {
Util.cancelAnimFrame(this._animId);
this._inProgress = false;
this.fire("end");
},
_easeOut: function (t) {
return 1 - Math.pow(1 - t, this._easeOutPower);
},
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 源码详解
- 类结构与初始化
- 继承自
Evented
:支持事件驱动,可触发start
、step
、end
事件。
- 启动动画(
run
方法)
- 关键参数:
easeLinearity
:控制缓动曲线的线性度。值越小,缓动效果越陡峭(默认0.5
)。duration
:动画持续时间,单位秒。
- 逻辑:
- 停止已有动画。
- 记录初始位置和位移。
- 触发
start
事件。 - 启动动画循环。
- 停止动画(
stop
方法)
- 作用:立即停止动画,并将元素定位到目标位置。
_step(true)
:参数true
表示对最终位置进行四舍五入,避免亚像素渲染问题。
- 动画循环(
_animate
与_step
方法)
作用:通过
requestAnimFrame
实现动画循环,调用_step
方法。动画帧处理(
_step
方法)Util.requestAnimFrame
:封装requestAnimationFrame
,用于浏览器的重绘,优化动画性能。elapsed/duration
:计算动画进度(0 到 1)_easeOut
:根据缓动函数计算实际进度
- 位置更新(
_runFrame
方法)
- **
progress
**:缓动函数修正后的进度值。 pos
:通过初始位置加位移乘以进度计算得出。- **
round
**:用于停止动画时确保位置为整数,防止模糊渲染。
- 动画结束(
_complete
方法)
- 清理资源:取消未执行的动画帧,触发
end
事件。
- 缓动函数(
_easeOut
方法)- 公式:
1 - (1 - t)^power
。 - 行为分析:
t
从0
到1
,输出从0
到1
。power
越大,动画初期变化越慢,后期加速越明显。- 默认
easeLinearity = 0.5
时,power = 2
,即二次缓出(类似 CSSease-out
)。
- 公式:
# 关键设计思想
性能优化:
- 使用
requestAnimationFrame
实现平滑动画,避免卡顿。 - 每帧计算量小,仅涉及基本数学运算和 DOM 操作。
- 使用
灵活配置:
- 支持自定义持续时间和缓动效果。
- 通过
easeLinearity
参数控制缓动曲线形态。
事件驱动:
- 提供
start
、step
、end
事件,便于外部监听动画状态。 - 例如:在
step
事件中可实时更新其他关联元素。
- 提供
# 使用场景
- 地图平移动画:平滑移动地图容器。
- 标记移动:动态调整标记位置。
- 控件过渡:展开/收起工具栏时的动画效果。
# 参数示例
**默认缓动(
easeLinearity = 0.5
)**:_easeOutPower = 2
,动画效果为二次缓出。
**线性动画(
easeLinearity = 1
)**:_easeOutPower = 1
,进度与时间线性相关。
**陡峭缓动(
easeLinearity = 0.2
)**:_easeOutPower = 5
,动画初期缓慢,后期快速完成。
# 潜在问题与注意事项
浏览器兼容性:
requestAnimationFrame
需要兼容旧浏览器(如 IE9+)。DomUtil.setPosition
需确保使用兼容的定位方式(如transform
)。
性能影响:
- 频繁启停动画可能导致内存泄漏,需合理调用
stop()
。
- 频繁启停动画可能导致内存泄漏,需合理调用
亚像素渲染:
- 最终位置四舍五入可避免元素模糊,但可能影响平滑度。
# 总结
PosAnimation
类通过 requestAnimationFrame
实现平滑的位置过渡,支持自定义持续时间和缓动函数,通过事件通知外部动画的状态变化(start
, step
, end
)。关键点在于缓动函数的计算和动画循环的管理。
编辑 (opens new window)
上次更新: 2025/03/21, 10:31:51