Jinuss's blog Jinuss's blog
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

Web、WebGIS技术博客
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 框架

  • core模块

  • dom模块

    • DomEvent
    • DomUtil
    • DomEvent.Pointer
    • DomEvent.DoubleTap
    • Draggable拖拽实现
    • PosAnimation位置动画介绍
      • 概述
      • 源码分析
        • 源码实现
        • 源码详解
        • 关键设计思想
        • 使用场景
        • 参数示例
        • 潜在问题与注意事项
      • 总结
  • control

  • geometry

  • geo

  • layer

  • Map

  • 《Leaflet源码》笔记
  • dom模块
东流
2025-03-20
目录

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

# 源码详解

  1. 类结构与初始化
  • 继承自Evented:支持事件驱动,可触发start、step、end事件。
  1. 启动动画(run方法)
  • 关键参数:
    • easeLinearity:控制缓动曲线的线性度。值越小,缓动效果越陡峭(默认 0.5)。
    • duration:动画持续时间,单位秒。 ​
  • 逻辑:
    1. 停止已有动画。
    2. 记录初始位置和位移。
    3. 触发 start 事件。
    4. 启动动画循环。
  1. 停止动画(stop方法)
  • 作用:立即停止动画,并将元素定位到目标位置。
  • _step(true):参数true表示对最终位置进行四舍五入,避免亚像素渲染问题。
  1. 动画循环(_animate与_step方法)
  • 作用:通过requestAnimFrame实现动画循环,调用_step方法。

  • 动画帧处理(_step方法)

    • Util.requestAnimFrame:封装requestAnimationFrame,用于浏览器的重绘,优化动画性能。
    • elapsed/duration:计算动画进度(0 到 1)
    • _easeOut:根据缓动函数计算实际进度
  1. 位置更新(_runFrame方法)
  • ​**progress**:缓动函数修正后的进度值。
  • pos:通过初始位置加位移乘以进度计算得出。
  • ​**round**:用于停止动画时确保位置为整数,防止模糊渲染。
  1. 动画结束(_complete方法)
  • 清理资源:取消未执行的动画帧,触发end事件。
  1. 缓动函数(_easeOut方法)
    • ​公式:1 - (1 - t)^power。
    • ​行为分析:
      • t 从 0 到 1,输出从 0 到 1。
      • power 越大,动画初期变化越慢,后期加速越明显。
      • 默认 easeLinearity = 0.5 时,power = 2,即二次缓出(类似 CSS ease-out)。 ​

# 关键设计思想

  1. ​性能优化:

    • 使用 requestAnimationFrame 实现平滑动画,避免卡顿。
    • 每帧计算量小,仅涉及基本数学运算和 DOM 操作。
  2. 灵活配置:

    • 支持自定义持续时间和缓动效果。
    • 通过 easeLinearity 参数控制缓动曲线形态。
  3. 事件驱动:

    • 提供 start、step、end 事件,便于外部监听动画状态。
    • 例如:在 step 事件中可实时更新其他关联元素。 ​

# 使用场景

  • ​地图平移动画:平滑移动地图容器。
  • 标记移动:动态调整标记位置。
  • 控件过渡:展开/收起工具栏时的动画效果。 ​

# 参数示例

  1. **​默认缓动(easeLinearity = 0.5)**​:

    • _easeOutPower = 2,动画效果为二次缓出。 ​
  2. **线性动画(easeLinearity = 1)**​:

    • _easeOutPower = 1,进度与时间线性相关。
  3. **陡峭缓动(easeLinearity = 0.2)**​:

    • _easeOutPower = 5,动画初期缓慢,后期快速完成。 ​

# 潜在问题与注意事项

​

  1. 浏览器兼容性:

    • requestAnimationFrame 需要兼容旧浏览器(如 IE9+)。
    • DomUtil.setPosition 需确保使用兼容的定位方式(如 transform)。 ​
  2. 性能影响:

    • 频繁启停动画可能导致内存泄漏,需合理调用 stop()。
  3. ​亚像素渲染:

    • 最终位置四舍五入可避免元素模糊,但可能影响平滑度。

# 总结

PosAnimation 类通过 requestAnimationFrame 实现平滑的位置过渡,支持自定义持续时间和缓动函数,通过事件通知外部动画的状态变化(start, step, end)。关键点在于缓动函数的计算和动画循环的管理。

编辑 (opens new window)
上次更新: 2025/03/21, 10:31:51
Draggable拖拽实现
Control

← Draggable拖拽实现 Control→

最近更新
01
GeoJSON
05-08
02
Circle
04-15
03
CircleMarker
04-15
更多文章>
Theme by Vdoing | Copyright © 2024-2025 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式