JS完美运动框架:解决多属性同时运动的定时器清除问题
本文深入分析JS运动框架中多属性同时运动时出现的异常现象。当宽度提前达到目标值时,由于共用一个定时器且判断逻辑缺陷,会导致高度和不透明度等属性运动提前终止。通过引入标志位flag检测所有属性是否均到达目标值,完善了运动框架的停止逻辑,确保动画完整执行。
本文通过具体实例解析JavaScript运动特效中的运动框架实现,旨在提供清晰的参考代码与逻辑说明。
运动框架的缺陷分析
在之前的《js运动特效之同时运动》一文中,我们指出该运动框架仍存在一个关键缺陷。为了明确这一问题,需要对前文的程序代码进行适当调整。
oDiv.onmouseover = function () {
//startMove(oDiv,{width:300,height:300,opacity:30});
startMove(oDiv,{width:204,height:300,opacity:30});
}
假设在鼠标移入事件中,我们将目标宽度从300修改为204,观察此时动画的表现会发生什么变化?

观察运行结果可以发现,当鼠标移入时,只有宽度成功动画至预期的204,而高度和不透明度并未达到各自的目标值(分别为300和0.3)。造成这一现象的原因在于代码逻辑中的特定问题。
if(objAttr == json[attr]){
clearInterval(obj.timer);
if(fn){
fn();
}
}
原代码中的if语句仅判断当前操作的属性objAttr是否等于目标值,一旦相等即清除定时器。这意味着只要有一个属性到达目标值,就会触发清除定时器的操作。由于宽度从200变为204的速度较快,它率先达到目标值;而此时高度和不透明度仍在动画过程中。因为width、height和opacity共用同一个定时器,一旦宽度达标触发清除操作,高度和不透明度的动画也会随之停止,导致它们永远无法到达最终目标值。
完美运动框架的解决方案
针对上述问题,我们需要寻找解决方案。
解决方法一:在启动定时器时,引入一个标志位var flag,用于监测所有运动属性是否均已到达目标值。
function startMove(obj,json,fn) {
clearInterval(obj.timer);
obj.timer = setInterval(function () {
var flag = true;// 一进入定时器设置标志位
},30);
}
var flag = true;// 表示所有的运动都到达目标值
var flag = false;// 表示有的运动没有到达目标值
解决方法二:在检测运动是否停止的逻辑中进行修正。
if(objAttr != json[attr]){// 判断是否所有的运动都达到目标值
// 进入if语句,说明至少有一个 运动未到达目标值
flag = false;
}
具体实现如下:在for...in循环中遍历每一个需要变化的属性(如width、height、opacity等)。只要发现任何一个属性尚未到达目标值,就将flag设置为false。在for...in循环结束后,检查flag的值:如果flag仍为true,则说明所有运动属性均已到达目标值,此时才可以安全地清除定时器。
for(attr in json){
}
if(flag){// 再次判断flag的值
clearInterval(obj.timer);
if(fn){
fn();
}
}
定时器每30毫秒执行一次。每次进入定时器函数时,首先初始化var flag =true,随后向下执行代码,每一轮循环都要进行状态判断。
if(objAttr != json[attr]){ ... }逻辑用于检查属性是否未达标,只要有一个运动未到达目标值,就会执行if语句并将flag设为false。当定时器最后一次执行时,如果发现所有属性均已到达目标值,则if条件不成立,flag不会被设为false,因此最后一次进入定时器时flag始终为true。此时满足if(flag){ ... }的条件,进而进入if(flag){ ... }执行清除操作。
经过上述优化,运动框架已趋于完美。以下是实际运行效果展示。

完整代码实现
完整的测试代码如下:
HTML部分和CSS部分与上一篇完全一致,仅JavaScript代码有所调整,具体代码如下:
<script>
window.onload = function(){
var oDiv = document.getElementById('div1');
oDiv.onmouseover = function () {
//startMove(oDiv,{width:300,height:300,opacity:30});
startMove(oDiv,{width:204,height:300,opacity:30});
}
oDiv.onmouseout = function () {
startMove(oDiv,{width:200,height:200,opacity:100});
}
}
function getStyle(obj,attr){
return getComputedStyle ? getComputedStyle(obj,false)[attr] : obj.currentStyle[attr];
}
function startMove(obj,json,fn) {
clearInterval(obj.timer);
obj.timer = setInterval(function () {
var bStop = true;
for(attr in json){
// 1. 取得当前的值(可以是widht,height,opacity等的值)
var objAttr = 0;
if(attr == "opacity"){
objAttr = Math.round(parseFloat(getStyle(obj,attr))*100);
}else{
objAttr = parseInt(getStyle(obj,attr));
}
// 2.计算运动速度
var iSpeed = (json[attr] -objAttr)/10;
iSpeed = iSpeed>0 ?Math.ceil(iSpeed):Math.floor(iSpeed);
// 3. 检测所有运动是否到达目标
if(objAttr != json[attr]){
bStop = false;
}
if(attr == "opacity"){
obj.style.filter = 'alpha(opacity:'+(objAttr+iSpeed)+')';
obj.style.opacity = (objAttr+iSpeed)/100;
}else{
obj.style[attr] = objAttr+iSpeed+'px';// 需要又.属性名的形式改成[]
}
}
if(bStop){ // 表示所有运动都到达目标值
clearInterval(obj.timer);
if(fn){
fn();
}
}
},30);
}
</script>
至此,我们可以将该运动框架独立封装为单独的JS文件,以便在后续项目中直接调用和复用。
如果您对JavaScript其他特效感兴趣,推荐查看本站专题:《JavaScript运动效果与技巧汇总》、《JavaScript切换特效与技巧总结》、《JavaScript查找算法技巧总结》、《JavaScript动画特效与技巧汇总》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》。
希望本文关于完美运动框架的阐述能对您的JavaScript程序设计有所帮助。
ES6模块化中js文件怎么用import和export交互
2026-11-05 17:00:48
JS读取本地文件时报错或无响应的常见原因
2026-11-05 16:32:31
let、const与var的区别:JavaScript变量声明最佳实践
2026-11-05 14:16:38
JS中直接定义静态二维数组的字面量写法
2026-11-05 14:16:08
JS 怎么查找字符串中所有匹配项的位置
2026-11-05 14:15:53
