js运动框架 step by step

开启setInterval定时器之前,请先清除之前的定时器

window.onload = function() {
    var btn = document.getElementById('btn');
    var oDiv = document.getElementById('div1');
    var timer = null;

    btn.onclick = function() {
        clearInterval(timer);                   // 避免定时器叠加
        var timer = setInterval(function() {    // 设置定时器,定义动画
            if (oDiv.offsetLeft > 300) {        // 动画的结束条件
                clearInterval(timer);
            }else {                             // 动画的内容
                oDiv.style.left = oDiv.offsetLeft + 10 + 'px';
            }
        }, 30);
    };
};

用函数封装动画

var timer = null;
// elem: 运动的元素
// iTarget: 运动的目标
// iSpeed: 动画的速度
function animate(elem, iTarget, iSpeed) {
    clearInterval(timer);
    timer = setInterval(function() {
        var offsetLeft = elem.offsetLeft;
        if(offsetLeft > iTarget) {
            clearInterval(timer);
        }else {
            elem.style.left = offsetLeft + iSpeed + 'px';
        }
    }, 30);
}

运动框架的基本步骤:

1. 清除定时器

2. 开启定时器,计算速度

3. 判断停止条件,执行运动

根据目标判断速度方向

var timer = null;
// elem: 运动的元素
// iTarget: 运动的目标
// iSpeed: 速度
function animate(elem, iTarget, iSpeed) {
    

    clearInterval(timer);    
    timer = setInterval(function() {
        var offsetLeft = elem.offsetLeft;
// 计算速度
    iSpeed = offsetLeft < iTarget ? iSpeed : - iSpeed;
if(offsetLeft === iTarget) {
            clearInterval(timer);
        }else {
            elem.style.left = offsetLeft + iSpeed + 'px';
        }
    }, 30);
}

淡入淡出,改变元素的透明度。需要有一个变量保存透明度值,用来和速度加减,然后赋值给元素的样式,从而实现淡入淡出。

var timer = null;
var opacity = 30;                   // 默认透明度
function animate(elem, iTarget, iSpeed) {
    // 计算速度
    iSpeed = opacity < iTarget ? iSpeed : - iSpeed;

    clearInterval(timer);    
    timer = setInterval(function() {
        if(opacity === iTarget) {
            clearInterval(timer);
        }else {
            opacity += iSpeed;      // 改变当前透明度
            elem.style.filter = 'alpha(opacity:' + opacity + ')';
            elem.style.opacity = opacity / 100;
        }
    }, 30);
}

缓冲运动,改变速度值,每次累加的速度值变小,物体改变的距离越来越变小。

物体运动逐渐变慢,最后停止。

对于速度:距离越远,速度越大;速度 = (目标点-当前值)/ 缩放系数

问题:1. 速度需要取整(正向速度向上取整,负向速度向下取整)原因是样式的像素会舍去小数部分

2. 潜在问题,目标值不是整数时(跟随页面滚动的缓冲侧边栏)

var timer = null;
function animate(elem, iTarget) {

    clearInterval(timer);    
    timer = setInterval(function() {        
        var offsetLeft = elem.offsetLeft;
        // 对正向速度向上取整,对负向速度向下取整
        var iSpeed = (iTarget - offsetLeft) / 8;
        iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);
        if(offsetLeft === iTarget) {                // 运动停止条件
            clearInterval(timer);
        }else {
            elem.style.left = offsetLeft + iSpeed + 'px';
        }
    }, 30);
}

运动终止条件

匀速运动,与目标点足够近(距离小于速度时,可把目标设为物体的终点);

利用绝对值可以避免判断方向问题

缓冲运动,与目标点重合;

多物体运动

每个运动物体都能开启一个属于自己的定时器。做法把定时器作为物体的属性,这样清理时针对自己。

参数的传递,物体与目标值

关键点,所有变量不能共享。(如把定时器变量作为物体的属性)

function animate(elem, iTarget) {

    clearInterval(elem.timer);    
    elem.timer = setInterval(function() {        
        var offsetWidth = elem.offsetWidth;
        // 对正向速度向上取整,对负向速度向下取整
        var iSpeed = (iTarget - offsetWidth) / 8;
        iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);
        if(offsetWidth === iTarget) {                // 运动停止条件
            clearInterval(elem.timer);
        }else {
            elem.style.width = offsetWidth + iSpeed + 'px';
        }
    }, 30);
}

多物体淡入淡出时,也出现同样的情况。必须针对每一个物体设置透明度属性(不能共享,只能独享)

function animate(elem, iTarget) {

    clearInterval(elem.timer);    
    elem.timer = setInterval(function() {        
        
        // 对正向速度向上取整,对负向速度向下取整
        var iSpeed = (iTarget - elem.opacity) / 8;
        iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);
        if(elem.opacity === iTarget) {                // 运动停止条件
            clearInterval(elem.timer);
        }else {
            elem.opacity += iSpeed;
            elem.style.filter = 'alpha(opacity:'+ elem.opacity +')';
            elem.style.opacity = elem.opacity / 100;
        }
    }, 30);
}

物体大小属性(offsetWidth / offsetHeight)的Bug。

原因:offsetWidth包含了border的宽,对拥有边框的div不等于其width属性

解决办法:获取真正的width值。

// 返回指定属性的属性值
// 返回值是字符串数字(带有单位)
function getStyle(elem, attr) {
    var value = null;
    if(elem.currentStyle) {
        value = elem.currentStyle[attr];
    }else {
        value = getComputedStyle(elem, null)[attr];
    }
    return value;
}

任意值运动

高度、宽度、透明度、位置。抽象animate函数

function animate(elem, attr, iTarget) {

    clearInterval(elem.timer);    
    elem.timer = setInterval(function() {        
        var iCur = getStyle(elem, attr);                // 获取当前attr属性值
        if (attr === 'opacity') {
            iCur = parseFloat(iCur * 100);
        }
        iCur = parseInt(iCur);                          // 转换成整形,待计算

        var iSpeed = (iTarget - iCur) / 8;              // 缓冲运动计算每次速度
        iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);

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

链式运动

简单地说,一个动画结束时另一个动画开始。(利用回调函数)

function animate(elem, attr, iTarget, callback) {

    clearInterval(elem.timer);    
    elem.timer = setInterval(function() {        
        var iCur = getStyle(elem, attr);                // 获取当前attr属性值
        if (attr === 'opacity') {
            iCur = parseFloat(iCur * 100);
        }
        iCur = parseInt(iCur);                          // 转换成整形,待计算

        var iSpeed = (iTarget - iCur) / 8;              // 缓冲运动计算每次速度
        iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);

        if (iCur === iTarget) {
            clearInterval(elem.timer);
            callback && callback();
        }else {
            if (attr === 'opacity') {
                iCur += iSpeed;
                elem.style.filter = 'alpha(opacity:'+ iCur +')';
                elem.style.opacity = iCur / 100;
            }else {
                elem.style[attr] = iCur + iSpeed + 'px';
            }        
        }
    }, 30);
}
window.onload = function() {
    var oBtn = document.getElementById('btn');
    var oDiv = document.getElementById('div1');

    oBtn.onclick = function() {
        animate(oDiv, 'left', 800, function() {                 // 向右滑动到800px
            animate(oDiv, 'width', 300, function() {            // 然后,宽度增大到300
                animate(oDiv, 'height', 400, function() {       // 然后,高度增大到400
                    animate(oDiv, 'opacity', 100);              // 最后,透明度为不透明
                });
            });
        });
    };

};

多属性同时改变

目前的运动框架每次运动只能改变一种属性的值.可以通过传递属性/目标值组成的字面量,来同时改变多个属性。

// 返回指定属性的属性值
function getStyle(elem, attr) {
    var value = null;
    if(elem.currentStyle) {
        value = elem.currentStyle[attr];
    }else {
        value = getComputedStyle(elem, null)[attr];
    }
    return value;
}

// attrs 多属性组成的字面量对象
function animate(elem, attrs, callback) {

    clearInterval(elem.timer);    
    elem.timer = setInterval(function() {        
        bStop = true;                                       // 检查所有的属性是否已经达到目标
        for(var attr in attrs) {                            // 遍历每一个属性
            
            var iCur = getStyle(elem, attr);                // 获取当前attr属性值
            if (attr === 'opacity') {
                iCur = parseFloat(iCur * 100);
            }
            iCur = parseInt(iCur);                          // 转换成整形,待计算

            var iSpeed = (attrs[attr] - iCur) / 8;          // 缓冲运动计算每次速度
            iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);

            if (iCur !== attrs[attr]) {
                bStop = false;
                if (attr === 'opacity') {
                    iCur += iSpeed;
                    elem.style.filter = 'alpha(opacity:'+ iCur +')';
                    elem.style.opacity = iCur / 100;
                }else {
                    elem.style[attr] = iCur + iSpeed + 'px';
                } 
            }
        }
        if (bStop) {            // 所有属性值已达到给定的目标值
            clearInterval(elem.timer);
            callback && callback();
        };
    }, 30);
}

window.onload = function() {
    var oBtn = document.getElementById('btn');
    var oDiv = document.getElementById('div1');

    oBtn.onclick = function() {
        animate(oDiv, { 500, height: 600, opacity: 100});
    };

};

分享到例子

主要根据目标判断速度的正负,从而在鼠标滑入滑出时候进行运动或恢复效果

<div id="div1">

<div id="share_btn">分享到</div>

</div>

body {

margin: 0;

padding: 0;

}

#div1 {

100px;

height: 200px;

background-color: #aaf6f8;

/* 动画的div设置为绝对定位 */

position: absolute;

top: 40%;

left: -100px;

margin-top: -100px;

}

/* 分享按钮,固定宽度用于定位,高度由内容决定 */

#share_btn {

28px;

padding: 8px 0;

background-color: #00F;

text-align: center;

color: #FFF;

position: absolute;

right: -28px;

top: 50%;

margin-top: -40px;

}

window.onload = function() {
    var oDiv = document.getElementById('div1');
    var share_btn = document.getElementById('share_btn');
    // 展示分享到信息
    oDiv.onmouseover = share_btn.onmouseover = function() {
        animate(oDiv, 0, 10);
    };
    // 恢复分享到状态
    oDiv.onmouseout = share_btn.onmouseout = function() {
        animate(oDiv, -100, 10);
    };
};
var timer = null;
// elem: 运动的元素
// iTarget: 运动的目标
// iSpeed: 速度
function animate(elem, iTarget, iSpeed) {
    // 计算速度
    iSpeed = elem.offsetLeft < iTarget ? iSpeed : - iSpeed;

    clearInterval(timer);    
    timer = setInterval(function() {
        var offsetLeft = elem.offsetLeft;
        if(offsetLeft === iTarget) {
            clearInterval(timer);
        }else {
            elem.style.left = offsetLeft + iSpeed + 'px';
        }
    }, 30);
}
推荐智能社Javascript系列视频教程
原文地址:https://www.cnblogs.com/mackxu/p/animate.html