JavaScript运动基础学习记录

运动需要用到Math对象中的以下方法:
Math.abs(number):绝对值
Math.ceil(number):向上取整
Math.floor(number):向下取整
Math.round(number):四舍五入

匀速运动

匀速运动概要

在匀速运动中可以能回遇到以下BUG:
1.运动不会停止。
2.当速度取某些值的时候会无法停止运动。
3.到达位置后再点击会继续运动。
4.重复点击速度加快。
具体的解决方法见下面代码部分。

运动框架核心关键点:
1.在开始运动时,关闭已有定时器。
2.把运动和停止隔开(if/else)。

匀速运动停止条件:当距离目标点足够近时停止。

具体见下方代码实例。

匀速运动基础

物体匀速运动到指定位置停止:

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
    <style>
      #div1 {
        width: 200px;
        height: 200px;
        background-color: blue;
        position: absolute;
        top: 50px;
        left: 0px;
      }
    </style>
    <script>
      var timer = null;

      function startMove(){
        var oDiv = document.getElementById('div1');
        var speed = 7;//设定运动速度
        //解决重复点击运动速度加快问题
        clearInterval(timer);
        timer = setInterval(function (){
          //解决不会停止问题
          //新问题:当速度取某些值的时候会无法停止运动。
          //原因:由于计时器是每隔设定的时间就执行一次函数,而物体运动的原理是
          //计时器每执行一次,物体的某一边距(取决于运动方向)就会加上速度值,
          //从而物体开始运动,由于if中的判断语句比较值写死了(如下为==300),
          //所以当速度取某些值的时候,会造成物体边距一次次加速度值永远达不到
          //if判断语句中的写死的值(例如下面的例子,当速度值取为7的时候oDiv.offsetLeft
          //永远不可能达到300,因为300不可以整除7,速度值可以累加到294和301,
          //永远到不了300,因此物体会一直运动无法停止)
          //解决方法:把if判断语句写死的值改为取值范围,如下:
          //if(oDiv.offsetLeft == 300) ---> if(oDiv.offsetLeft >= 300)

          //if (oDiv.offsetLeft == 300){
          //  clearInterval(timer);
          //}

          //解决当速度取某些值的时候会无法停止运动问题
          //新问题:当物体到达指定位置后再次点击还会继续运动
          //原因:当物体停止以后再次点击,会再次开始执行计时器,虽然计时器中的判断条件满足
          //计时器停止条件,并执行清空计时器的代码,但只是终止计时器的下一次执行函数,而这一
          //计时器中的函数会执行完,即继续执行一次物体运动的代码(oDiv.style.left = oDiv.offsetLeft + speed +'px';)
          //因此当物体到达指定位置后再次点击还会继续运动。
          //解决方法:使用else语句把物体运动代码包起来,和上面的if判断组成if-else语句,即
          //如果满足条件则停止计时器,否则继续运动。

          if(oDiv.offsetLeft>=300){
            clearInterval(timer);
          }
          //oDiv.style.left = oDiv.offsetLeft + speed +'px';

          //解决物体到达位置后再点击还会运动问题
          //新问题:重复点击运动速度加快
          //原因:每点击一次就会启动一回计时器,造成计时器叠加,从而导致速度加快。
          //解决方法:在启动计时器之前先清空所有计时器,具体做法为,在设置计时器的
          //代码之前,写一条清除计时器的代码(具体内容见上方)
          else{
            oDiv.style.left = oDiv.offsetLeft + speed +'px';
          }

        },30);
      }
    </script>
  </head>
  <body>
    <input id='btn' type="button" name="" value="开始运动" onclick = 'startMove()'>
    <div id='div1'>

    </div>
  </body>
</html>

匀速运动基础升级

物体精准匀速运动到指定目标:

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
    <style>
      #div1 {width:100px; height:100px; background:red; position:absolute; left:600px; top:50px;}
      #div2 {width:1px; height:300px; position:absolute; left:300px; top:0; background:black;}
      #div3 {width:1px; height:300px; position:absolute; left:100px; top:0; background:black;}
    </style>
    <script>
      var timer = null;
      function startMove(target){
        var oDiv = document.getElementById('div1');
        clearInterval(timer);
        timer = setInterval(function (){
          var speed = 0;
          if(oDiv.offsetLeft<target){
            speed = 7;
          }else{
            speed = -7;
          }
          //解决匀速运动无法精准停止在目标位置问题
          //原理:当物体距离目标位置足够近的时候停止,然后直接把目标值赋给物体某一边距值(取决于运动方向)。
          //使用Math.abs()绝对值,来判断足够近的位置(足够近的条件为在-speed到speed之间)。
          if(Math.abs(target-oDiv.offsetLeft)<=7){
            clearInterval(timer);
            oDiv.style.left = target + 'px';
          }else{
            oDiv.style.left = oDiv.offsetLeft + speed + 'px';
          }
        },30);
      }
    </script>
  </head>
  <body>
    <input type="button" value="到100" onclick="startMove(100)" />
    <input type="button" value="到300" onclick="startMove(300)" />
    <div id="div1"></div>
    <div id="div2"></div>
    <div id="div3"></div>
  </body>
</html>

匀速运动框架封装

      var timer = null;
      function startMove(target){
        var oDiv = document.getElementById('div1');
        clearInterval(timer);
        timer = setInterval(function (){
          var speed = 0;
          if(oDiv.offsetLeft<target){
            speed = 7;
          }else{
            speed = -7;
          }
          //解决匀速运动无法精准停止在目标位置问题
          //原理:当物体距离目标位置足够近的时候停止,然后直接把目标值赋给物体某一边距值(取决于运动方向)。
          //使用Math.abs()绝对值,来判断足够近的位置(足够近的条件为在-speed到speed之间)。
          if(Math.abs(target-oDiv.offsetLeft)<=7){
            clearInterval(timer);
            oDiv.style.left = target + 'px';
          }else{
            oDiv.style.left = oDiv.offsetLeft + speed + 'px';
          }
        },30);
      }

缓冲运动

缓冲运动概要

运动形式:逐渐变慢,最后停止。
核心原理:距离越远速度越大(速度由距离决定)
缓冲运动计算公式:
速度 = ( 目标值 - 当前值 ) / 缩放系数
解释:可以通过更改缩放系数调节速度快慢。

缓冲运动可能会遇到的BUG:
1.无法准确运动到指定的目标位置。
2.物体反向运动时无法准确运动到指定目标位置
具体解决方法见下面代码实例。

缓冲运动基础

物体缓冲运动到指定位置:

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
    <style>
      #div1 {
        width:200px;
        height:200px;
        background-color:blue;
        position: absolute;
        left:600px;
        top:50px;
      }
      #div2 {
        width:1px;
        height:300px;
        position: absolute;
        left:300px;
        top:0px;
        background-color:black;
      }
    </style>
    <script>
      //缓冲运动速度计算公式:(目标值-当前值)/缩放系数

      var timer = null
      function startMove(){
        var oDiv = document.getElementById('div1');
        //clearInterval(timer);
        timer = setInterval(function (){
          //问题:无法准确运动到指定的目标位置(本程序目标位置是300px,物体停止位置为296px)
          //原因:计算机所能识别的最小单位即使px(像素),在本程序中当物体运动到296px的时候
          //根据缓冲运动速度计算公式((目标值-当前值)/缩放系数)得出速度(speed)为0.4px,
          //计算机会把小数部分舍去,因此速度变为0。
          //解决方法:使用Math.ceil()对速度speed向上取整。
          var speed = (300-oDiv.offsetLeft)/10;

          //解决无法准确运动到指定目标位置问题
          //新问题:当物体反向运动时无法准确运动到指定目标位置
          //原因:当目标值小于当前值的时候,物体会反向运动,根据缓冲运动速度计算公式,得到的
          //速度为负值,由于负数越接近0越大,此时向上取整把速度置为0,所以当物体反向运动是无
          //法准确运动到指定目标位置。
          //解决方法:判断物体运动方向,如果是正向运动使用Math.ceil()向上取整,如果反向运动
          //使用Math.floor()反向取整。
          //speed = Math.ceil(speed);

          //解决当物体反向运动是无法准确运动到指定目标位置问题
          speed = speed>0?Math.ceil(speed):Math.floor(speed);
          oDiv.style.left = oDiv.offsetLeft + speed + 'px';
          document.title = oDiv.offsetLeft + ',' + speed;
        },30);
      }
    </script>
  </head>
  <body>
    <input type="button" name="" value="开始运动" onclick="startMove()">
    <div id='div1'>

    </div>
    <div id='div2'>

    </div>
  </body>
</html>

缓冲运动升级——多物体缓冲运动

BUG:
1.多物体运动在一个物体运动期间触发另一个物体运动时导致前面物体运动停止。
具体解决方式见下方代码。

多物体缓冲运动核心关键点:所有东西(定时器,速度,透明度等)都不能共用。
具体做法:通过属性与运动对象进行绑定。

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title></title>
    <style>
      div {width:100px;height:50px;background-color:blue;margin:10px;}
    </style>
    <script>
      window.onload = function (){
        var oDiv = document.getElementsByTagName('div');
        for(var i=0;i<oDiv.length;i++){
          //解决多物体运动在一个物体运动期间触发另一个物体运动时导致前面物体运动停止问题
          //原因:见下方。
          //解决方法:使每个物体拥有单独的定时器,具体做法是把定时器设为物体的属性。
          //详见下方第一步、第二步、第三步
          oDiv[i].timer = null;//第一步
          oDiv[i].onmouseover = function (){
            startMove(this,400);
          };
          oDiv[i].onmouseout = function (){
            startMove(this,100);
          };
        }
      }
      //var timer = null;
      function startMove(obj,target){
        //原因:当初发另一个物体运动时会重新执行下面这条清除定时器的代码,由于所有物体共用一个定时器,
        //因此当一个物体运动还未停止时,触发另一个物体运动会导致中止前面物体的运动。
        //clearInterval(timer);
        clearInterval(obj.timer);第三步
        obj.timer = setInterval(function (){//第二步
          var speed = (target - obj.offsetWidth)/6;
          speed = speed>0?Math.ceil(speed):Math.floor(speed);
          if(obj.offsetWidth == target){
            clearInterval(timer);
          }else{
            obj.style.width = obj.offsetWidth + speed + 'px';
          }
        },30);
      }
    </script>
  </head>
  <body>
    <div></div>
    <div></div>
    <div></div>
  </body>
</html>

多物体缓冲运动框架

function startMove(obj,target){
        clearInterval(obj.timer);
        obj.timer = setInterval(function (){
          var speed = (target - obj.offsetWidth)/6;
          speed = speed>0?Math.ceil(speed):Math.floor(speed);
          if(obj.offsetWidth == target){
            clearInterval(timer);
          }else{
            obj.style.width = obj.offsetWidth + speed + 'px';
          }
        },30);
      }

任意值运动框架

原有运动框架问题:
只能让某个固定的值运动起来,如果想让其他值运动起来,需要修改程序。

getStyle()

offset属性获取的是盒模型的值,因此当运动对象带有边框等样式的时候会出现bug。因此获取运动对象的方法改用封装的getStyle方法。

//封装getStyle方法
function getStyle(obj,name){
	if(obj.currentStyle){
		return obj.currentStyle[name];
	}else{
		return getComputedStyle(obj,false)[name];
	}
}
任意值运动框架基础版

新增运动属性参数

function getStyle(obj, name){
        if(obj.currentStyle){
          return obj.currentStyle[name];
        }else{
          return getComputedStyle(obj,false)[name];
        }
      }
      function startMove(obj,name,target){
        clearInterval(obj.timer);
        obj.timer = setInterval(function (){
          var cur = parseInt(getStyle(obj,name));
          var speed = (target-cur)/6;
          speed = speed>0?Math.ceil(speed):Math.floor(speed);
          if(cur == target){
            clearInterval(obj.timer);
          }else{
            obj.style[name] = cur + speed + 'px';
          }
        },30);
      }
任意值运动框架升级版

基础版解决了物体长宽高等以px为单位的属性的运动问题,升级版新增了对物体透明度的支持。
BUG:使用透明度运动时物体轻微闪烁或透明度无法达到指定值
解决方法看下方。

function getStyle(obj, name){
        if(obj.currentStyle){
          return obj.currentStyle[name];
        }else{
          return getComputedStyle(obj,false)[name];
        }
      }
      function startMove(obj,name,target){
        var cur = 0;
        clearInterval(obj.timer);
        obj.timer = setInterval(function (){
          if(name == 'opacity'){
            //解决使用透明度运动时物体轻微闪烁或透明度无法达到指定值的问题。
            //原因:计算机无法精确存储小数,只能存储小数的近似值。
            //解决方法:使用Math.round方法把小数转为整数
            cur = Math.round(parseFloat(getStyle(obj,name))*100);
          }else{
            cur = parseInt(getStyle(obj,name));
          }
          var speed = (target-cur)/6;
          speed = speed>0?Math.ceil(speed):Math.floor(speed);
          if(cur == target){
            clearInterval(obj.timer);
          }else{
            if(name == 'opacity'){
              obj.style.filter = 'alpha(opacity:'+(cur + speed)+')';
              obj.style.opacity = (cur + speed)/100;
            }
            obj.style[name] = cur + speed + 'px';
          }
        },30);
      }
任意值运动框架进阶——链式运动框架

任意值运动框架无法完成按照指定顺序运动(先边长后变宽等),链式运动框架可很好的解决这个问题。
在任意值运动框架的基础上再增加一个回调函数参数。
当运动停止时,执行函数。

function getStyle(obj, name){
  if(obj.currentStyle){
    return obj.currentStyle[name];
  }else{
    return getComputedStyle(obj,false)[name];
  }
}
function startMove(obj,name,target,fnEnd){
  var cur = 0;
  clearInterval(obj.timer);
  obj.timer = setInterval(function (){
    if(name == 'opacity'){
      cur = Math.round(parseFloat(getStyle(obj,name))*100);
    }else{
      cur = parseInt(getStyle(obj,name));
    }
    var speed = (target-cur)/6;
    speed = speed>0?Math.ceil(speed):Math.floor(speed);
    if(cur == target){
      clearInterval(obj.timer);
      if(fnEnd) fnEnd();
    }else{
      if(name == 'opacity'){
        obj.style.filter = 'alpha(opacity:'+(cur + speed)+')';
        obj.style.opacity = (cur + speed)/100;
      }
      obj.style[name] = cur + speed + 'px';
    }
  },30);
}

任意值运动框架进阶2——完美运动框架

前面的所有框架无法实现运动对象的多个属性值同时运动。完美运动框架可解决这个问题。
完美运动框架把链式运动框架的参数中的属性名参数和属性目标值参数去掉,改用json对象作为参数。

function getStyle(obj,name){
  if(obj.currentStyle){
    return obj.currentStyle[name];
  }else{
    return getComputedStyle(obj,false)[name];
  }
}

function startMove(obj,json,fnEnd){
    clearInterval(obj.timer);
    obj.timer = setInterval(function (){
      var flag = true; //假设都达到目标值
      for(var i in json){
        console.log(i + ':' + json[i]);
        var cur = 0;
        if(i == 'opacity'){
          cur = Math.round(parseFloat(getStyle(obj,i))*100);
        }else{
          cur = parseInt(getStyle(obj,i));
        }
        var speed = (json[i] - cur)/6;
        speed = speed>0?Math.ceil(speed):Math.floor(speed);
        if(cur != json[i]){
          flag = false;
        }
        if(i == 'opacity'){
          obj.style.filter = 'alpha(opacity:'+(cur+speed)+')';
          obj.style.opacity = (cur + speed)/100;
        }else{
          obj.style[i] = cur + speed + 'px';
        }
      }
      if(flag){
        clearInterval(obj.timer);
        if(fnEnd) fnEnd();
      }
    },30);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值