countTo插件在Safari运行缓慢且速度配置失效兼容性问题
问题根源
- 第一是浏览器的定时器强制节流规则:HTML标准明确要求,嵌套层级超过5层的
setInterval/setTimeout最小触发间隔不得低于4ms,Safari对超短间隔定时器的节流策略比Chrome更严格,你代码中设置的refreshInterval:1(1ms触发一次)在Safari中实际触发间隔会被强制拉长到10~16ms,和设备屏幕刷新帧率对齐,远达不到设定的更新频率。 - 第二是插件本身的逻辑缺陷:原有代码预先用
speed/refreshInterval算出总更新次数,每次更新固定叠加一个步进值,完全依赖定时器的触发精度控制总动画时长。一旦定时器实际触发间隔偏离设定值,总时长就会完全失控。按你的配置,动画需要更新300次,若Safari中每次更新实际间隔为16ms,总时长就会达到4800ms,是预设300ms的16倍,直接表现为动画速度极慢、speed配置完全不生效。
修复方法
从两个维度调整即可解决全浏览器兼容性问题:
- 废弃固定步进+固定更新次数的实现逻辑,改为基于时间差计算动画进度:记录动画启动的高精度时间戳,每次更新时计算已流逝时长占总
speed的比例,通过比例换算当前应该显示的数值,从根本上摆脱对定时器触发精度的依赖。 - 弃用1ms的超短刷新间隔,直接使用
requestAnimationFrame驱动动画,和浏览器渲染帧率保持同步,性能更好,也不会触发定时器节流规则,Safari兼容性极佳。
修复后的完整插件代码如下:
(function($) { $.fn.countTo = function(options) { options = $.extend({}, $.fn.countTo.defaults, options || {}); return $(this).each(function() { const el = this; const animStartTime = performance.now(); const startVal = options.from; const endVal = options.to; const duration = options.speed; function frame(currentTime) { const elapsed = currentTime - animStartTime; const progress = Math.min(elapsed / duration, 1); const currentVal = startVal + (endVal - startVal) * progress; $(el).html(currentVal.toFixed(options.decimals)); if (typeof options.onUpdate === 'function') { options.onUpdate.call(el, currentVal); } if (progress < 1) { requestAnimationFrame(frame); } else { if (typeof options.onComplete === 'function') { options.onComplete.call(el, endVal); } } } requestAnimationFrame(frame); }); }; $.fn.countTo.defaults = { from: 1.0, to: 2.0, speed: 300, decimals: 1, onUpdate: null, onComplete: null }; })(jQuery); jQuery(function($) { $('.counter-up').countTo({ from: 1.00, to: 2.00, decimals: 1, speed: 300 }); });
原有HTML结构(jQuery引入、计数元素)不需要做任何修改。修复后动画会严格按照设定的speed时长完成,在Safari下的流畅度和速度表现和Chrome完全一致。
如果需要兼容IE9及以下不支持
requestAnimationFrame的老旧浏览器,可以加简单polyfill回退到setInterval,注意将回退的刷新间隔设为16ms即可,不要使用小于10ms的间隔配置,避免触发浏览器节流。
内容的提问来源于stack exchange,提问作者DNa
相关产品推荐
相关产品推荐

