jQuery 的 animate 方法支持多种参数组合,比如可以把速度、缓动名称、回调函数分开传,也可以打包成一个对象。这种灵活性依赖于内部一个核心工具函数,它就是 speed。speed 函数承担了参数标准化的工作,把开发者随性书写的参数解析成动画引擎可以消费的规范结构。要自定义缓动函数,先得理解这个解析过程,否则容易在传参顺序或选项对象上踩坑。

jQuery.speed() 的参数解析逻辑
speed 函数位于 jQuery 源码内部,主要负责处理 animate 方法传入的速度、缓动名称和完成回调。它的核心任务是将三种完全不同的调用形式统一成一个对象。当第一个参数是对象时,会直接使用 jQuery.extend 复制对象属性;当第一个参数是数值或字符串时,才进入分派逻辑,把后续参数依次视为 easing 和 complete。整个过程看似简单,但衍生出不少边界情况。
下面是一段简化后的 speed 函数实现,可以帮助理解它的分派与默认值填充逻辑。
function speed(speed, easing, fn) {
var opt = speed && typeof speed === 'object' ? jQuery.extend({}, speed) : {
complete: fn || !fn && easing || jQuery.isFunction(speed) && speed,
duration: speed,
easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
};
opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === 'number' ? opt.duration :
opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[opt.duration] : jQuery.fx.speeds._default;
if (opt.queue == null || opt.queue === true) {
opt.queue = 'fx';
}
opt.old = opt.complete;
opt.complete = function() {
if (jQuery.isFunction(opt.old)) {
opt.old.call(this);
}
if (opt.queue) {
jQuery.dequeue(this, opt.queue);
}
};
return opt;
}
代码中 opt.duration 这一行处理了三种情况:如果 jQuery.fx.off 为 true 则直接将持续时间设为 0,用于关闭动画;如果传入的是数字则直接使用;如果是 fast 或 slow 字符串,则去 jQuery.fx.speeds 映射中查找,找不到就用默认值 400 毫秒。而 complete 回调被包装了一层,目的是在动画结束后自动出队,保证队列中的下一个动画能继续执行。
实际使用中,可以通过 jQuery.speed 直接查看解析结果,虽然这不是公开 API,但方便调试参数传递。
// 常见写法
var opt1 = jQuery.speed(800, 'swing', function() {
console.log('动画完成');
});
console.log(opt1);
// 输出: { duration: 800, easing: 'swing', queue: 'fx', complete: [Function], ... }
var opt2 = jQuery.speed({
duration: 600,
easing: 'linear',
complete: function() {}
});
console.log(opt2.duration); // 600
注册自定义缓动函数的正确姿势
jQuery 的缓动函数统一挂在 jQuery.easing 对象上。内置只有两个:线性函数 linear 直接返回进度,摆动函数 swing 使用余弦变换让动画首尾慢中间快。自定义缓动非常直接,就是往 jQuery.easing 对象上添加一个新的方法。这个方法的入参是 0 到 1 的进度值,返回值表示对应时间点的位移比例,通常也落在 0 到 1 之间,但某些弹性或过冲效果会短暂超过边界。
以下示例注册了一个三次缓入缓出函数,它在前半段加速、后半段减速,视觉效果比 swing 更柔和。
// 注册自定义缓动函数
jQuery.easing.easeInOutCubic = function (p) {
if (p < 0.5) {
return 4 * p * p * p;
}
return 1 - Math.pow(-2 * p + 2, 3) / 2;
};
// 使用自定义缓动
$('#box').animate({ left: 300 }, 1000, 'easeInOutCubic', function() {
console.log('移动完成');
});
缓动函数内部应当只依赖入参 p 做数学计算,不要使用 this,也不要读取或修改外部动画状态。如果需要在动画结束时执行逻辑,应该通过 complete 回调,而不是在缓动函数里做额外操作。另外要注意命名冲突,不要覆盖内置的 linear 和 swing,否则会影响所有使用默认缓动的动画。
除了常见的三次曲线,还可以实现带回弹效果的 easeOutBack,它会让元素在终点前稍微超过目标位置再回弹,常用于强调交互反馈。
jQuery.easing.easeOutBack = function (p) {
var c1 = 1.70158;
var c3 = c1 + 1;
return 1 + c3 * Math.pow(p - 1, 3) + c1 * Math.pow(p - 1, 2);
};
$('#box').animate({
left: 300
}, {
duration: 800,
easing: 'easeOutBack',
complete: function() {
console.log('带过冲效果的动画结束');
}
});
缓动函数的调用时机与动画性能优化
jQuery 动画每执行一帧,都会根据已过去的时间和总持续时间计算出一个原始进度 p,然后调用对应的缓动函数得到 eased 值,再用这个 eased 值去插值计算当前属性数值。缓动函数每秒被调用几十次,因此它本身的计算量会直接反映到动画流畅度上。一个函数里多出几次 Math.pow 通常没有大问题,但如果在缓动函数内读写 DOM、强制触发重排,或者创建大量临时对象,就可能造成明显卡顿。
下面模拟了 jQuery 动画循环中缓动函数被调用的过程,便于理解其执行频率与位置。
// jQuery 动画循环中的简化调用逻辑
function tick() {
var currentTime = Date.now();
var elapsed = currentTime - startTime;
var p = Math.min(elapsed / duration, 1);
var eased = jQuery.easing[easingName](p);
var currentValue = startValue + (endValue - startValue) * eased;
element.style.left = currentValue + 'px';
if (p < 1) {
requestAnimationFrame(tick);
}
}
优化自定义缓动函数时,首先应当将函数引用在动画开始前取出,避免每帧用字符串去 jQuery.easing 对象中查找。其次,不要在缓动函数内部声明新的数组或对象,尽量复用外部变量。如果多个元素使用同一条缓动曲线,可以让它们共享同一个函数引用,减少内存分配。对于单纯的线性或摆动动画,直接使用内置函数即可,不必为了自定义而自定义。
理解 speed 的解析机制和缓动函数的运行原理后,就能在 jQuery 动画中灵活定制曲线,同时避免参数传递错误和性能陷阱。自定义缓动函数本质上就是给动画引擎提供一个纯数学映射,只要保证输入输出稳定、计算轻量,它就能配合动画队列持续稳定地工作。
jQuery.speed缓动函数Easing修改时间:2026-09-23 12:53:53