• 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系列视频教程
  • 相关阅读:
    Linux远程复制scp和rsync区别
    一个线上问题的思考:Eureka注册中心集群如何实现客户端请求负载及故障转移?
    docker离线安装
    centos7 docker 宿主机不能访问容器问题解决
    nginx 或 tengine安装错误提示:./configure: error: the HTTP rewrite module requires the PCRE library
    redhat 安装中文 支持中文
    rpm忽略依赖,强制安装,加--force --nodeps ( warning: perl-Test-Nginx-0.28-1.el6_10.noarch.rpm: Header V4 RSA/SHA1 Signature, key ID d5edeb74: NOKEY error: Failed dependencies:)
    redhat6 安装openresty
    redhat6 Unable to establish SSL connection.
    Lua入门脚本(与Redis结合)
  • 原文地址:https://www.cnblogs.com/mackxu/p/animate.html
Copyright © 2020-2023  润新知