Sortable动画原理:Animation.js实现平滑过渡
【免费下载链接】Sortable 项目地址: https://gitcode.com/gh_mirrors/sor/Sortable
Sortable是一款功能强大的JavaScript拖拽排序库,其平滑的动画效果极大提升了用户体验。本文将深入解析Sortable中动画系统的核心实现,重点剖析src/Animation.js如何通过矩阵变换和帧动画技术,实现拖拽过程中元素的无缝过渡效果。
动画系统架构概览
Sortable的动画系统采用状态管理模式,通过src/Animation.js提供的AnimationStateManager工厂函数,为每个Sortable实例注入完整的动画控制能力。该系统主要包含三大核心模块:
- 状态捕获模块:记录元素拖拽前的初始位置
- 动画执行模块:计算并应用元素过渡动画
- 冲突解决模块:处理并发动画的中断与衔接
核心实现位于src/Sortable.js中,通过Object.assign(this, AnimationStateManager())将动画能力混入Sortable原型,使每个实例都能独立管理自身的动画状态。
状态捕获:captureAnimationState方法
动画系统的第一步是在拖拽开始前捕获所有元素的初始位置。src/Animation.js中的captureAnimationState方法通过以下步骤工作:
- 清空现有动画状态队列
- 遍历容器内所有可见子元素(排除幽灵元素)
- 调用src/utils.js中的
getRect方法获取元素边界矩形 - 存储元素引用与初始位置信息到
animationStates数组
关键代码实现:
captureAnimationState() {
animationStates = [];
if (!this.options.animation) return;
let children = [].slice.call(this.el.children);
children.forEach(child => {
if (css(child, 'display') === 'none' || child === Sortable.ghost) return;
animationStates.push({
target: child,
rect: getRect(child)
});
// 处理正在进行的动画补偿
let fromRect = { ...animationStates[animationStates.length - 1].rect };
if (child.thisAnimationDuration) {
let childMatrix = matrix(child, true);
if (childMatrix) {
fromRect.top -= childMatrix.f;
fromRect.left -= childMatrix.e;
}
}
child.fromRect = fromRect;
});
}
该方法特别处理了元素正在进行动画的情况,通过src/utils.js的matrix函数获取当前变换矩阵,对初始位置进行补偿计算,确保动画衔接自然。
动画执行:animateAll与animate方法
当元素位置发生变化时,src/Animation.js的animateAll方法会批量处理所有元素的过渡动画。其核心流程包括:
- 检查动画配置,无动画时直接触发回调
- 遍历所有捕获的元素状态
- 计算元素当前位置与目标位置的差异
- 调用
animate方法执行具体动画 - 统一管理动画完成回调
位置差异计算
动画系统通过src/utils.js的isRectEqual方法判断元素是否需要动画:
function isRectEqual(rect1, rect2) {
return Math.round(rect1.top) === Math.round(rect2.top) &&
Math.round(rect1.left) === Math.round(rect2.left) &&
Math.round(rect1.height) === Math.round(rect2.height) &&
Math.round(rect1.width) === Math.round(rect2.width);
}
只有当元素的位置或尺寸发生变化时,才会触发动画流程。
CSS变换实现
src/Animation.js的animate方法采用CSS Transform实现高性能动画:
animate(target, currentRect, toRect, duration) {
if (duration) {
css(target, 'transition', '');
css(target, 'transform', '');
let elMatrix = matrix(this.el),
scaleX = elMatrix && elMatrix.a,
scaleY = elMatrix && elMatrix.d,
translateX = (currentRect.left - toRect.left) / (scaleX || 1),
translateY = (currentRect.top - toRect.top) / (scaleY || 1);
// 设置初始偏移
css(target, 'transform', 'translate3d(' + translateX + 'px,' + translateY + 'px,0)');
// 触发重排
this.forRepaintDummy = repaint(target);
// 应用过渡动画
css(target, 'transition', 'transform ' + duration + 'ms' + (this.options.easing ? ' ' + this.options.easing : ''));
css(target, 'transform', 'translate3d(0,0,0)');
// 动画结束清理
(typeof target.animated === 'number') && clearTimeout(target.animated);
target.animated = setTimeout(function () {
css(target, 'transition', '');
css(target, 'transform', '');
target.animated = false;
}, duration);
}
}
这段代码采用了"先偏移后过渡"的经典动画技巧,通过设置初始translate3d值到目标位置的偏移量,再触发过渡到translate3d(0,0,0),实现元素的平滑移动。使用translate3d而非top/left可以触发GPU加速,提升动画性能。
冲突解决:实时动画中断处理
在快速拖拽场景中,经常需要中断正在进行的动画并开始新动画。src/Animation.js通过以下机制处理这种冲突:
- 记录元素当前动画状态(
thisAnimationDuration) - 计算剩余动画时间(
calculateRealTime函数) - 基于当前位置与目标位置的线性关系,推断新的动画起点
if (target.thisAnimationDuration) {
// 检查是否在同一动画路径上
if (
isRectEqual(prevFromRect, toRect) &&
!isRectEqual(fromRect, toRect) &&
// 验证当前位置是否在新旧路径的连线上
(animatingRect.top - toRect.top) /
(animatingRect.left - toRect.left) ===
(fromRect.top - toRect.top) /
(fromRect.left - toRect.left)
) {
// 计算剩余动画时间
time = calculateRealTime(animatingRect, prevFromRect, prevToRect, this.options);
}
}
这种处理确保了在快速拖拽时,元素动画不会出现跳跃或闪烁,始终保持视觉连续性。
动画时序控制
Sortable动画系统通过精细的时序控制,确保所有元素动画同步完成。src/Animation.js实现了统一的动画完成回调机制:
clearTimeout(animationCallbackId);
if (!animating) {
if (typeof(callback) === 'function') callback();
} else {
animationCallbackId = setTimeout(function() {
if (typeof(callback) === 'function') callback();
}, animationTime);
}
通过跟踪最长动画时间(animationTime),确保所有元素动画完成后才触发回调,为后续操作(如数据更新)提供准确的时序保证。
实践应用:动画参数配置
开发者可以通过Sortable初始化选项配置动画效果:
new Sortable(el, {
animation: 150, // 动画持续时间(毫秒)
easing: "cubic-bezier(0.175, 0.885, 0.32, 1.275)" // 缓动函数
});
默认配置下,动画系统使用src/utils.js的throttle函数限制动画触发频率,平衡性能与流畅度。
总结与扩展
Sortable的动画系统通过src/Animation.js与src/utils.js的紧密配合,实现了高性能、高可靠性的元素过渡效果。其核心优势在于:
- 状态驱动设计:通过捕获-计算-应用的清晰流程,确保动画可控性
- CSS硬件加速:采用translate3d触发GPU加速,提升动画性能
- 冲突智能处理:动态调整动画参数,解决并发动画冲突
对于高级用户,可以通过修改src/Animation.js的animate方法,实现自定义动画效果,如添加缩放或旋转变换。同时,tests/single-list.html提供了动画效果的基础测试场景,可用于验证自定义动画的兼容性。
通过深入理解Sortable的动画原理,开发者不仅可以更好地使用该库,还能将这些技术应用到其他前端动画场景中,构建更加流畅自然的用户体验。
【免费下载链接】Sortable 项目地址: https://gitcode.com/gh_mirrors/sor/Sortable
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考





