javascript 运动与游戏开发

一、学习运动框架作用

  •  web页面开发的过程中,如何与用户进行友好,有趣的交互,是我们必须考虑的问题,比如:

        导航条中滑动的动画特效

        点击加入购物车按钮通过抛物线

        加入右侧购物车的动画特效

        网页游戏的开发:微信打飞机,打砖块等

二、运动原理

  •  Js运动:
  •   就是让 web 上 DOM 元素动起来,改变其自身的位置属性,比如高宽,左边距,上边距,透明度等。
  • 动画的原理就是把不同状态的物体,串成连续的样子。
  • js动画也一样,不同状态的DOM,用定时器控制,就能得到动画效果。

人眼能够识别的最小的时间间隔是18帧。
【注】电影院电影24帧。

  • 如何实现运动:
  • 1.运动的物体使用绝对定位
  • 2.通过改变定位物体的属性(left、right、top、bottom)值来使物体移动。例如
  • 向右或左移动可以使用offsetLeft(offsetRight)来控制左右移动。
  • 步骤:
  1. 开始运动前,先清除已有定时器 (因为:是连续点击按钮,物体会运动越来越快,造成运动混乱)
  2. 开启定时器,计算速度
  3. 把运动和停止隔开(if/else),判断停止条件,执行运动

三、定时器

  • 在javascritp中,有两个关于定时器的专用函数,它们是倒计定时器、循环定时器。

3.1. 倒计定时器:timer=setTimeout(函数名,delaytime);

  •  倒计时定时器就是在指定时间后触发事件,只是作用一次。
  • 一般用于页面上只需要触发一次的的情况,比如点击某按钮后页面在一定时间后跳转到相应的站点,也可以用于判断一个浏览者是不是你的站点上的“老客”,如果不是,你就可以在5秒或者10秒后跳转到相应的站点,然后告诉他以后再来可以在某个地方按某一个按钮就可以快速进入。

3.2. 循环定时器:timer=setInterval(函数名,delaytime);

  • 循环定时器就是在间隔时间到来时反复触发事件,不停地作用。

  • 一般用于站点上需要从复执行的效果,比如一个javascript的滚动条或者状态栏,也可以用于将页面的背景用飞雪的图片来表示。这些事件需要隔一段时间运行一次。

  • function()是定时器触发时要执行的是事件的函数,可以是一个函数,也可以是几个函数,或者javascript的语句也可以,单要用;隔开;

  • delaytime则是间隔的时间,以毫秒为单位

3.3 删除定时器 clearTimeout(timename) clearInterval(timename)

  • clearTimeout(timename)关闭倒计时定时器
  • clearInterval(timename)来关闭循环定时器

四、运动研究

4.1. 运动:匀速运动(让物体动起来)

  1. 对定时器的使用
  2. 给DIV加绝对定位
  3. offsetLeft
window.onload = function(){
    var oMenu = document.getElementById("menu");
    oMenu.onmouseenter = function(){
        //-100 => 0
        startMove(0);
    }
    oMenu.onmouseleave = function(){
        //0 => -100
        startMove(-100);
    }
    var timer = null;
    function startMove(iTarget){
        var oMenu = document.getElementById("menu");
        var speed = 10;
        //1、每次启动定时器将上一次定时器关闭
        clearInterval(timer);
        timer = setInterval(function(){
            if(oMenu.offsetLeft < iTarget){
                speed = Math.abs(speed);
            }else{
                speed = -Math.abs(speed);
            }

            //2、运动和停止分开
            if(oMenu.offsetLeft == iTarget){
                clearInterval(timer);
            }else{
                oMenu.style.left = oMenu.offsetLeft + speed + 'px';
            }
        }, 30);
    }
}

淡入淡出:

window.onload = function(){
    var oImg = document.getElementById("img1");
    oImg.onmouseenter = function(){
        //30 => 100
        startMove(100);
    }
    oImg.onmouseleave = function(){
        //100 => 30
        startMove(30);
    }
    /* 
        类似于透明度这类型的css属性,使用中间变量替代计算。
     */
    var alpha = 30;
    var timer = null;
    function startMove(iTarget){
        var oImg = document.getElementById("img1");
        var speed = 2;
        clearInterval(timer);
        timer = setInterval(function(){
            if(alpha > iTarget){
                speed = -Math.abs(speed);
            }else{
                speed = Math.abs(speed);
            }
            //1、取当前值
            if(alpha == iTarget){
                clearInterval(timer);
            }else{
                alpha += speed;
                //給当前图片上设置透明度,考虑浏览器兼容
                oImg.style.opacity = alpha / 100;
                oImg.style.filter = "alpha(opacity=" + alpha + ")";
                document.title = alpha;
            }
        }, 30);
    }
}

 

1.问题:到达某个特定位罝停止
解决:做判断,符合条件时关掉定时器(存定时器timer)
  速度变慢(一般不动时间,而是改数字-速度)
  用变量存速度

2.问题:取7时,offsetLeft没有等于300的时候,div停不下来
解决:>=300 //停在 301

3.问题:到300后点击按钮还继续走
原因:点击按钮,执行函数,开定时器(执行当前函数一至少执行一次)
解决:加else (没有到达目标之前才执行)

4.问题:连续点击,速度变快
原因:每点击一次就开一个定时器,点击几次就有几个定时器同时工作
解决:保证每次只有一个定时器工作,先cearlnterval ()

4.2、变速运动

 逐渐变慢,最后停止
距离越远速度越大
速度由距离决定
速度=(目标值-当前值)/缩放系数

window.onload = function(){
     var oBtn = document.getElementById("btn1");
     var oDiv = document.getElementById("div1");
     oBtn.onclick = function(){
         startMove(500);
     }
     var timer = null;
     function startMove(iTarget){
         var oDiv = document.getElementById("div1");
         var oTxt1 = document.getElementById("txt1");
         clearInterval(timer);
         timer = setInterval(function(){
              //计算速度
             var speed = (iTarget - oDiv.offsetLeft) / 8;
             speed = Math.ceil(speed);
             if(oDiv.offsetLeft == iTarget){
                 clearInterval(timer);
             }else{
                 oDiv.style.left = oDiv.offsetLeft + speed + 'px';
                 txt1.value += oDiv.offsetLeft + ", " + speed + "\n";
             }
         }, 30);
     }
 }
  • 缓存菜单
window.onload = function(){
    var oMenu = document.getElementById("menu");
    //获取当前的目的值
    var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
    var windowHeight = document.documentElement.clientHeight || document.body.clientHeight;
    var iH = parseInt(scrollTop + (windowHeight - oMenu.offsetHeight) / 2);
    startMove(iH);
    window.onscroll = function(){
        var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
        var windowHeight = document.documentElement.clientHeight || document.body.clientHeight;
        var iH = parseInt(scrollTop + (windowHeight - oMenu.offsetHeight) / 2);

        startMove(iH);
    }
}

var timer = null;
function startMove(iTarget){
    var oMenu = document.getElementById("menu");
    clearInterval(timer);
    timer = setInterval(function(){
        //计算速度
      

 var speed = (iTarget - oMenu.offsetTop) / 8;
                  speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

                  //运动和停止分开
                  if(oMenu.offsetTop == iTarget){
                      clearInterval(timer);
                  }else{
                      oMenu.style.top = oMenu.offsetTop + speed + 'px';
                      document.title = oMenu.offsetTop + ", " + iTarget;
                  }

              }, 30);
          }
1.问题:并没有真正到达300
原因:速度只剩0.9 像素是屏幕能够显示的最小单位,并不会四舍五入掉

Math.ceil() 向上取整
Math.floor() 向下取整
2.问题:向左走,又差一块–Math.floor ()
判断:三目 speed=speed>0 ? Math.ceil ( speed ): Math.floor ( speed )

4.3、多物体运动

 多个div ,鼠标移入变宽
运动框架传参obj,知道让哪个物体动起来
用到缓冲一定要取整

 多物体运动:

window.onload = function(){
    var aDivs = document.getElementsByTagName("div");
    for(var i = 0; i < aDivs.length; i++){
        aDivs[i].onmouseover = function(){
            startMove(this, 300);
        }
        aDivs[i].onmouseout = function(){
            startMove(this, 100);
        }
    }
}

/* 
    原因是:我们整个页面上只有一个定时器。
    解决:让每一个运动的物体,独立拥有自己的定时器。

*/
// var timer = null;

function startMove(node, iTarget){
    clearInterval(node.timer);
    node.timer = setInterval(function(){
        //确定缓冲运动的速度
        var speed = (iTarget - node.offsetWidth) / 8;
        speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

        if(node.offsetWidth == iTarget){
            clearInterval(node.timer);
        }else{
            node.style.width = node.offsetWidth + speed + 'px';
        }

    }, 30);
}

多物体-淡入淡出:

window.onload = function(){
   var aDivs = document.getElementsByTagName("div");
   for(var i = 0; i < aDivs.length; i++){
       aDivs[i].alpha = 30;
       aDivs[i].onmouseover = function(){
           startMove(this, 100);
       }
       aDivs[i].onmouseout = function(){
           startMove(this, 30);
       }
   }
}


/* 
   中间变量:多个物体在进行透明度变化的时候,公用的是一个中间变量。

   结论:任何的变量在多物体运动中,都不能共用。
*/
// var alpha = 30;

function startMove(node, iTarget){
  clearInterval(node.timer);
  node.timer = setInterval(function(){
      var speed = (iTarget - node.alpha) / 8;
      speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

      if(node.alpha == iTarget){
          clearInterval(node.timer);
      }else{
          node.alpha += speed;

          node.style.opacity = node.alpha / 100;
          node.style.filter = "alpha(opacity=" + node.alpha + ")";
      }

  }, 30);
}

1.问题:div没运动回去 //清除前一个定时器
原因:只有一个定时器
解决:加物体上的定时器,使每个物体都有一个定时器。定时器作为物体属性
多个div淡入淡出
首先关闭物体上的定时器
经验:多物体运动框架所有东西都不能共用

2.问题:不是因为定时器,而是因为alpha
解决:作为属性附加到物体上 /不以变量形式存在

3.问题:offset 的 bug 加border变宽
原因:offsetWith并不是真正的width ,它获取的是盒模型尺寸
解决:躲着 宽度扔到行间,parselnt ( oDiv.style.width )
进一步解决: getStyle ( obj, name ) currentStyle , getComputedStyle
加border ,只要offset就有问题 去掉offset
示例,多物体运动框架:

4.4.任意值运动

  • 【offset问题】
 window.onload = function(){
     var oDiv = document.getElementById("div1");
     setInterval(function(){
         // alert(oDiv.offsetWidth); //width + border
         oDiv.style.width = oDiv.offsetWidth - 1 + 'px';
     }, 30);
 }
  • 用getStyle方法来获取当前有效眼样式
  • node.currentStyle ? node.currentStyle[cssStr] : getComputedStyle(node)[cssStr];
window.onload = function(){
  var oDiv = document.getElementById("div1");
  setInterval(function(){
      //获取当前值
      var iCur = parseInt(getStyle(oDiv, "width"));
      alert(iCur);
      oDiv.style.width = iCur - 1 + 'px';
  }, 30);
}
//获取当前有效样式浏览器兼容的写法
function getStyle(node, cssStr){
  return node.currentStyle ? node.currentStyle[cssStr] : getComputedStyle(node)[cssStr];
}
  • 任意值运动的单位分为透明度和px。
  • px单位的任意值
  • 【多物体多样式】
window.onload = function(){
    var aDivs = document.getElementsByTagName("div");
    aDivs[0].onclick = function(){
        //宽变成300
        startMove(this, "width", 300);
    }

    aDivs[1].onclick = function(){
        //高变成300
        startMove(this, "height", 300);
    }

    aDivs[2].onclick = function(){
        //marginLeft => 300
        startMove(this, "marginLeft", 300);
    }

    aDivs[3].onclick = function(){
        //fontSize => 100
        startMove(this, "fontSize", 100);
    }

    aDivs[4].onmouseover = function(){
        startMove(this, "opacity", 100);
    }
    aDivs[4].onmouseout = function(){
        startMove(this, "opacity", 30);
    }
}

//startMove(oDiv, "width", 300);
function startMove(node, attr, iTarget){
    clearInterval(node.timer);
    node.timer = setInterval(function(){
        //计算速度
        var iCur = null;
        if(attr == "opacity"){
            iCur = parseInt(parseFloat(getStyle(node, "opacity")) * 100);
        }else{
            iCur = parseInt(getStyle(node, attr))
        }
        
        var speed = (iTarget - iCur) / 8;
        speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

        if(iCur == iTarget){
            clearInterval(node.timer);
        }else{
            if(attr == "opacity"){
                iCur += speed;
                node.style.opacity = iCur / 100;
                node.style.filter = "alpha(opacity=" + iCur +  ")";
            }else{
                node.style[attr] = iCur + speed + 'px';
            }
        }
    }, 30);
}

4.5.链式运动

  • 链式运动
  • 在第一个动画结束的时候,开始第二个动画。
  • 【注】关键点,找到第一个动画结束的时候。
window.onload = function(){
    var aDivs = document.getElementsByTagName("div");
aDivs[0].onmouseover = function(){
 //嵌套function 实现链式运动
      startMove(this, "width", 300, function(){
          startMove(this, "height", 300, function(){
              startMove(this, "opacity", 100);
          })
      });     
  }
  aDivs[0].onmouseout = function(){
      
      startMove(this, "opacity", 30, function(){
          startMove(this, "height", 100, function(){
              startMove(this, "width", 100);
          })
      });
  }
}

回调函数
我们把函数当做参数传入,并且在合适调用的方式,叫做回调函数。在别的编程语言(C语言、C++)叫做函数指针。

function startMove(node, attr, iTarget, complete){//complete = show;
   clearInterval(node.timer);
   node.timer = setInterval(function(){
       //计算速度
       var iCur = null;
       if(attr == "opacity"){
           iCur = parseInt(parseFloat(getStyle(node, "opacity")) * 100);
       }else{
           iCur = parseInt(getStyle(node, attr))
       }
       
       var speed = (iTarget - iCur) / 8;
       speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);

       if(iCur == iTarget){
           clearInterval(node.timer);
           if(complete){
               complete.call(node);
           }
           // alert("运动结束了");
           /* 
               当运动结束以后,应该做什么的代码在这里不能写死。
               【注】封装函数,形参,根据函数不确定的值决定的。
               可以,将一段代码编写的权利交给别人。声明一个形参,这个形参是用来接收,从外面封装好的一个函数的。
           */
       }else{
           if(attr == "opacity"){
               iCur += speed;
               node.style.opacity = iCur / 100;
               node.style.filter = "alpha(opacity=" + iCur +  ")";
           }else{
               node.style[attr] = iCur + speed + 'px';
           }
       }
   }, 30);
}

 多出来的一个参数,只有传进去的时候才调用
鼠标移入变宽,结束之后弹出abc
先横向展开.再以向展开
鼠标移出,先变回不透明,变矮,变窄

五、封装运动框架

1. 认识运动(运动框架)
<1>每次启动定时器,将上一次定时器关闭
<2>运动和停止 if…else
2. 分享到菜单和淡入淡出
startMove(iTarget);
3. 缓冲运动
var speed = (iTarget - iCur) / 8;
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
4. 多物体运动
node.timer;
多物体淡入淡出:任何一个变量都不能公用
startMove(node, iTarget);
5. 多物体多样式运动
offset系列 透明度
startMove(node, attr, iTarget);
6. 链式运动
startMove(node, attr, iTarget, complete)
7. 完美运动
startMove(node, cssObject, complete);

六、运动框架的应用

1. banner图

  • 编写图片div

        
  • 设计div样式

  • js代码:
  • 调用startMove()函数
  • 【tips】将四张图片添加到末尾;若滚动四张结束,则设置left = 0px ; 回到第一张。

        

你可能感兴趣的:(javascript,开发语言,ecmascript)