JS完美运动框架:解决多属性同时运动的定时器清除问题

作者:青柠

时间:2026-10-23

浏览:273

本文深入分析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程序设计有所帮助。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章