美文网首页
JavaScript实现动画的简易框架

JavaScript实现动画的简易框架

作者: Jie_R | 来源:发表于2017-05-05 22:57 被阅读0次

运动的效果一般上都是在一定的时间内对其样式进行更改,从而在视觉上达到运动的感觉。这时候就要利用setInterval()在间隔时间内对其样式进行相应的改变。
想要一个物体运动一般需要原物体的位置,速度,运动到达的位置,运动的形式等。这些都会作为方法的参数进行传递。

前期准备

创建一个方法来进行获取该对象的属性值。需要的参数有两个:对象,属性名(运动的形式)。代码如下:

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

currentStyle是为了兼容IE6浏览器,兼容性情况如下

currentStyle.png

getComputedStyle兼容现在的大多数浏览器

getComputedStyle.png

方法的具体使用可以查看文档

1.单方向上的运动

html页面里面有三个li标签,用来演示效果。原始宽度为200px,高度为100px。

window.onload = function(){
    var lis = document.getElementsByTagName("li");
    for(var i = 0;i<lis.length;i++){
        lis[i].onmouseover = function(){
            startMove(this,10,400);
        }
        lis[i].onmouseout = function(){
            startMove(this,-10,200);
        }
    }
}
var timer = null;
function startMove(obj,speed,target){
    //一开始就进行清楚,是为了消除onmouseon和onouseover事件之间的影响
    //例如,当鼠标移上去后再移出来后就会发现li的宽度一直在10px之间不断的进行增加和减少的循环操作
    //这是因为onmouseover和onmouseout的setInterval还存在,固有下面一行来清楚之间的影响
    clearInterval(timer);
    timer = setInterval(function(){
        //如果已经达到了目标值,就停止
        if(obj.offsetWidth == target){
            clearInterval(timer);
        }else{
            obj.style.width = obj.offsetWidth + speed + "px";
        }
    },30)
}

这里先用宽度来进行展示。首先要弄懂style.width和offsetWidth的区别。

  • offsetWidth属性可以返回对象的padding+border+width属性值之和,style.width返回值就是定义的width属性值。
  • offsetWidth属性仅是可读属性,而style.width是可读写的。
  • offsetWidth属性返回值是整数,而style.width的返回值是字符串。
  • style.width仅能返回以style方式定义的内部样式表的width属性值。

startMove方法传入的是三个参数

  1. obj,对象参数
  2. speed,动画的变化速度
  3. target,变化所要达到的值

但是这里会有一个问题,就是timer是作为全局变量,每个li对象都是公用这么一个timer。
每个li运动之间的效果都会相互之间影响。问题如图所示:

相互之间的影响相互之间的影响

解决这个问题很简单,就是把全局变量改成诶个li对象自己的变量。也就是为对象添加一个timer属性。
改进后的代码如下:

window.onload = function(){
    var lis = document.getElementsByTagName("li");
    for(var i = 0;i<lis.length;i++){
        lis[i].timer = null; //为每个li对象添加timer属性
        lis[i].onmouseover = function(){
            startMove(this,10,400);
        }
        lis[i].onmouseout = function(){
            startMove(this,-10,200);
        }
    }
}
//下面的timer也要相应的改成obj.timer
function startMove(obj,speed,target){
    clearInterval(obj.timer);
    obj.timer = setInterval(function(){
        if(obj.offsetWidth == target){
            clearInterval(obj.timer);
        }else{
            obj.style.width = obj.offsetWidth + speed + "px";
        }
    },30)
}

2.根据传递的属性来改变运动的形式

上面的代码只能进行单方向的运动,如果想要进行高度上的变化,就要把代码中的width相应的改成height。
这样就显得过于麻烦,可以改造一下startMove函数,可以根据传递进去的属性来改变运动的形式。这时候就要用到前期准备中的getStyle(obj,attr)方法了.

window.onload = function(){
    var lis = document.getElementsByTagName("li");
    for(var i = 0;i<lis.length;i++){
        lis[i].timer = null;
        lis[i].onmouseover = function(){
            startMove(this,10,200,"height");
        }
        lis[i].onmouseout = function(){
            startMove(this,-10,100,"height");
        }
    }
}
function startMove(obj,speed,target,attr){
    clearInterval(obj.timer);
    obj.timer = setInterval(function(){
        //用getStyle方法来获取对应属性的属性值
        var iattr = parseInt(getStyle(obj,attr));
        if(iattr == target){
            clearInterval(obj.timer);
        }else{
            //这里不能够使用style.+"attr"的形式
            obj.style[attr] = iattr + speed + "px";
        }
    },30)
}

但是这里只有够对width或者height这样简单的变化,如果是opacity透明度的话就要进行相应的判断处理。(这里的opacity使用的是0~1)

function startMove(obj,speed,target,attr){
    clearInterval(obj.timer);
    obj.timer = setInterval(function(){
       if(iattr == target){
            clearInterval(obj.timer);
        }else{
            //进行属性的判断
            if(attr == "opacity"){
                obj.style.opacity = target;
            }else{
                obj.style[attr] = iattr + speed + "px";
            }
        }
    },30)
}

3.缓冲运动

上面的运动的速度都是固定,并且要向startMove方法里面传递速度值。固定的速度看起来可能太僵硬,加上缓冲感觉更好些。缓冲运动的实现就是运动的加速度不断地额变小,这里就是将速度的值不断的变小,并且不传递进来速度值,而是根据目标值和原有值之间的差来决定。

function startMove(obj, target, attr) {
    clearInterval(obj.timer);
    obj.timer = setInterval(function() {
        var iattr = null;
        var speed = null;
        if (attr == "opacity") {
            //将小数的情况转成整数,方便后面进行速度的变化
            iattr = Math.round(parseFloat(getStyle(obj, attr)) * 100);
            speed = (target * 100 - iattr) / 8;
        } else {
            iattr = parseInt(getStyle(obj, attr));
            speed = (target - iattr) / 8;
        }
        //如果出现小数的情况,浏览器会自动社区小数点,故而需要向上或者向下取整
        speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
        if (iattr == target) {
            clearInterval(obj.timer);
        } else {
            if (attr == "opacity") {
                obj.style.opacity = (iattr + speed) / 100;
            } else {
                obj.style[attr] = iattr + speed + "px";
            }
        }
    }, 30)
}

4.链式运动

上面的物体只能够执行一次运动,并没有第二次的运动。如何让其执行第一次执行变化后紧接着执行第二次的变化呢?这里让li的宽先变成400,紧接着高变为200。一开始想到的就是在鼠标事件里面再让其执行一个startMove()方法,情况如下:

lis[i].onmouseover = function() {
            startMove(this, 400, "width");
            startMove(this,200,"height");
        }

运行的时候会发现,只执行了第二个startMove方法。这是因为当第二个startMove方法执行的时候就会立刻把第一个的timer给清除了,所以只剩下第二个方法在执行。

解决的方法就是向startMove方法里面传递一个函数。

window.onload = function() {
    var lis = document.getElementsByTagName("li");
    for (var i = 0; i < lis.length; i++) {
        lis[i].timer = null;
        lis[i].onmouseover = function() {
            //第二个匿名function传递对象时不能够传递this,要将当前的this保存为一个变量再进行传递
            var li = this;
            startMove(this, 400, "width", function() {
                startMove(li, 200, "height");
            });
        }
        lis[i].onmouseout = function() {
            var li = this;
            startMove(this, 100, "height", function() {
                startMove(li, 200, "width");
            });
        }
    }
}
function startMove(obj, target, attr, fn) {
    clearInterval(obj.timer);
    obj.timer = setInterval(function() {
        var iattr = null;
        var speed = null;
        if (attr == "opacity") {
           iattr = Math.round(parseFloat(getStyle(obj, attr)) * 100);
            speed = (target * 100 - iattr) / 8;
        } else {
            iattr = parseInt(getStyle(obj, attr));
            speed = (target - iattr) / 8;
        }
        speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
        if (iattr == target) {
            clearInterval(obj.timer);
            //当上一个运动完成时执行下一个运动
            if (fn) {
                fn();
            }
        } else {
            if (attr == "opacity") {
                obj.style.opacity = (iattr + speed) / 100;
            } else {
                obj.style[attr] = iattr + speed + "px";
            }
        }

    }, 30)
}

5.同时运动

同时运动需要一次传递多个属性和目标,要达到这个目的可以使用json。就相当于使用键值对的形式,如{width:400,height:200}。这样又可以吧startMove的的参数减少一个。

window.onload = function() {
    var lis = document.getElementsByTagName("li");
    for (var i = 0; i < lis.length; i++) {
        lis[i].timer = null;
        lis[i].onmouseover = function() {
            //第二个匿名function传递对象时不能够传递this,要将当前的this保存为一个变量再进行传递
            var li = this;
            startMove(this, {
                width: 400,
                height: 200,
                opacity: 0.3
            });
        }
        lis[i].onmouseout = function() {
            startMove(this, {
                width: 200,
                height: 100,
                opacity: 1
            })
        }
    }
}
//只需要对json进行遍历即可,并且target相应的换成json[attr]
function startMove(obj, json, fn) {
    clearInterval(obj.timer);
    obj.timer = setInterval(function() {
        var iattr = null;
        var speed = null;
        for (var attr in json) {
            if (attr == "opacity") {
               iattr = Math.round(parseFloat(getStyle(obj, attr)) * 100);
                speed = (json[attr] * 100 - iattr) / 8;
            } else {
                iattr = parseInt(getStyle(obj, attr));
                speed = (json[attr] - iattr) / 8;
            }
            speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
            if (iattr == json[attr]) {
                clearInterval(obj.timer);
               if (fn) {
                    fn();
                }
            } else {
                if (attr == "opacity") {
                    obj.style.opacity = (iattr + speed) / 100;
                } else {
                    obj.style[attr] = iattr + speed + "px";
                }
            }
        }

    }, 30)
}

这里还有一个小问题,就是当一个属性的变化较小时,其他运动也到不到设置的目标值。解决这个问题只需要加入一个标志,用来判断运动是否达到当前目标值。

function startMove(obj, json, fn) {
    clearInterval(obj.timer);
    //运动是否完成的标志
    var flag = true;
    var iattr = null;
    var speed = null;
    obj.timer = setInterval(function() {
        for (var attr in json) {
            if (attr == "opacity") {
                iattr = Math.round(parseFloat(getStyle(obj, attr)) * 100);
                speed = (json[attr] * 100 - iattr) / 8;
            } else {
                iattr = parseInt(getStyle(obj, attr));
                speed = (json[attr] - iattr) / 8;
            }
            speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
            //运动为完成,flag为false
            if (iattr != json[attr]) {
                flag = false;
            }
            if (attr == "opacity") {
                obj.style.opacity = (iattr + speed) / 100;
            } else {
                obj.style[attr] = iattr + speed + "px";
            }
            //运动完成
            if (flag) {
                clearInterval(obj.timer);
                if (fn) {
                    fn();
                }
            }
        }
    }, 30)
}

这个简易框架基本上已经完成了。这篇文章总结自慕课网JS动画效果

相关文章

  • JavaScript实现动画的简易框架

    运动的效果一般上都是在一定的时间内对其样式进行更改,从而在视觉上达到运动的感觉。这时候就要利用setInterva...

  • 「DOM 编程」JavaScript 动画

    JavaScript 动画实现方式JavaScript 动画三要素定时器常见动画动画函数 JavaScript 动...

  • JS动画效果

    JavaScript 动画框架 框架封装 相信大家在很多门户网站上都可以看到动画的交互效果,通过这些动画生动地体现...

  • 微网站开发规范

    整体框架布局 JavaScript书写规范 CSS书写规范 整体框架布局采用seajs实现JavaScript的模...

  • Android源码相关分析(a)

    1、Android动画框架实现原理 android 动画框架分为三种类别:渐变动画、帧动画、属性动画Android...

  • JavaScript封装简易动画函数

    1、简单的右移函数:鼠标点击按钮,box向右移动一定的位置 实现效果: 2、封装同时解决左右移动的动画函数 上面的...

  • 实现动画方式深度解析

    实现动画方式深度解析(一) —— 播放GIF动画(一)实现动画方式深度解析(二) —— 播放GIF动画之框架FLA...

  • RPC框架的最简单实现

    一个最简单的RPC框架实现包含暴露服务、引用服务、Socket通讯三部分。 RPC框架简易实现 接口 接口实现 服...

  • 我的代码生活html简易框架

    html简易框架基本实现,小伙伴们可以自己动手改造哟!

  • express 框架的简易实现

    简易源码 测试

网友评论

      本文标题:JavaScript实现动画的简易框架

      本文链接:https://www.haomeiwen.com/subject/rlqaattx.html