源码分析之Leaflet中dom模块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. 类结构与初始化
  • 继承自Evented:支持事件驱动,可触发startstepend事件。
  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
    • ​行为分析
      • t01,输出从 01
      • power 越大,动画初期变化越慢,后期加速越明显。
      • 默认 easeLinearity = 0.5 时,power = 2,即二次缓出(类似 CSS ease-out)。

关键设计思想

  1. ​性能优化

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

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

    • 提供 startstepend 事件,便于外部监听动画状态。
    • 例如:在 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)。关键点在于缓动函数的计算和动画循环的管理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jinuss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值