JavaScript封装简易动画函数

  在学习javascript动画效果的过程,动画函数一定是少不了的,所以在初级学习的过程中,封装好一个动画函数可以直接调用能够帮我们省下更多的学习时间。下面是我一步步完善动画函数的过程。
1、简单的右移函数:鼠标点击按钮,box向右移动一定的位置
//封装右移动画函数
        function animateMoveRight(element,target){
            //通过offsetLeft获取当前位置的left值
            var left = element.offsetLeft;

            //开启定时器
            var timer = setInterval(function(){
                //当前位置每一次+5 实现匀速运动
                left += 5;
                //将每一次改变后的left值设置给元素
                element.style.left = left + 'px';
                //判断动画结束的标志 到达目标位置停止计时器
                if(left >= target){
                    clearInterval(timer);
                }    
            },30);
        }
        //在window.onload中调用封装好的动画函数
        window.onload =function () {
            var box = document.getElementById('box');

            //动态创建按钮
            for(var i = 200; i < 1000; i += 100){
                //创建按钮
                var button = document.createElement('button');            
                button.innerHTML = '右移' + i + 'px';
                //获取按钮对应的移动值
                button.index = i;
                //点击按钮
                button.onclick = function(){
                    // console.log(this.index);
                    //调用封装好的右移函数
                    animateMoveRight(box,this.index);
                }
                //添加按钮
                document.body.appendChild(button);
            }            
        }
    </script>

实现效果:


右移效果.png
2、封装同时解决左右移动的动画函数

上面的函数只能实现单向的向右移动,点击‘右移500px’按钮后,再点击‘右移200px’按钮无法回到200px处。下面实现这种效果。

//封装一个函数  同时解决左右移动的问题
        function animateMove(element,target){
            clearInterval(timer)
            var left = element.offsetLeft;

            //设置步长 表示一步动作的差值
            //通过比较element当前的left值和target值的大小,来确定平移方向
            var step = (target - left) / 10;

            var timer = setInterval(function(){
                //如果目标值大于当前的left值,step为正数,向右移动
                //如果目标值小于当前的left值,step为负数,向左移动
                left += step;
                box.style.left = left + 'px';

                //判断停止动画
                //比较差值,取绝对值,当两者的差值小于了步进值时,停止动画,
                if(Math.abs(target - left) <= Math.abs(step)){
                    clearInterval(timer);
                    element.style.left = target + 'px';
                }    
            },30);
        }

        
        //调用函数
        window.onload =function () {
            var box = document.getElementById('box');
            
            for(var i = 200; i < 1000; i += 100){
                var button = document.createElement('button');    

                button.innerHTML = '右移' + i + 'px';
                button.target = i;
                button.onclick = function(){
                    animateMove(box,this.target);
                }
                document.body.appendChild(button);
            }            
        }

效果展示:(当前在400px位置,点击200px按钮会回到200px位置)

左右移动.png
3、封装带有指定属性的动画函数

在设置动画的过程中,不只是左右移动那么简单,我们想要是的是想改变元素的什么属性就能够改变。在开始之前先了解怎么访问并获取css的属性。

3.1 访问css属性

我们知道element.style.xxx 只能够获取行内式属性,无法获取在style中设置的属性;offset获取的是本身实际获得的,没有定位,没有设left夜里可以获得。我们通过设置element.currentStyle.xxx(ie浏览器)或者window.getComputedStyle(element, null).xxx(其他浏览器)获得。封装函数如下:

1 //封装一个函数,用于获取某一个元素的某一条CSS属性值
 2         function getStyle(element, styleName){
 3             if(element.currentStyle){
 4                 return element.currentStyle[styleName];
 5             }else{
 6                 var computedStyle = window.getComputedStyle(element, null);
 7                 return computedStyle[styleName];
 8             }
 9         }
10 
11         window.onload = function(){
12             var box = document.getElementById('box');
13             var height = getStyle(box,'height');
14             console.log(height); //200px
15         }
3.2 封装带有指定属性的动画函数
//引入getStyle函数
        function getStyle(element, styleName){
            if(element.currentStyle){
                return element.currentStyle[styleName];
            }else{
                var computedStyle = window.getComputedStyle(element, null);
                return computedStyle[styleName];
            }
        }

        //封装带有指定属性的的动画函数 (元素名,属性名,目标值)
        var timer;
        function animate(element,styleName,target){
            clearInterval(timer);
            //获取该元素当前的属性
            var current = parseInt(getStyle(element,styleName));
            //设置步长 定值
            var step = (target - current) / 10;
            
            //开启动画设置滚动效果移动 
            timer = setInterval(function(){
                //通过步长 一点的的改变current  直到达到target值
                current += step;
                //判断动画结束的标志 
                //比较差值 当两者的差值小于了步进值时,停止动画 
                if(Math.abs(target - current) <= Math.abs(step)){
                    clearInterval(element.timer);
                    current = target;  //存在一点误差 强制将current归为目标值
                }

                //将改变后当前动画中的style值,设置给动画的元素
                element.style[styleName] = current + 'px';
            },30);
        }

        //调用
        window.onload = function(){
            var box =document.getElementById('box');
            
            animate(box,'width',500);
        } 
4、封装带有多个属性的动画函数(同时运动)

前面虽然能根据传入的属性参数改变元素运动,但是每次只能设置一种属性,如果同时调用只会显示最后一种效果。所以下面使用json参数传入多个属性。

 <script type="text/javascript">
    //json格式参考
    function f(){
        var json ={left:100,top:50}
        for(var key in json){
            console.log(key); //打印属性 left top
            console.log(json[key]); //打印属性值 100  50
        }
    }
    f();
    </script>
//获取属性的的网页中实际的(当前的)属性值
        function getStyle(element, styleName){
            if(element.currentStyle){
                return element.currentStyle[styleName];
            }else{
                var computedStyle = window.getComputedStyle(element, null);
                return computedStyle[styleName];
            }
        }

        //封装带有多个属性的的动画函数    利用json参数
        function animate(element,json){
            clearInterval(element.timer);
            //由于多个属性的运动  为了避免一个属性完成后就停止定时器的现象,所以设置isStop
            //是否停止动画,默认为false表示不停止
            var isStop = false;

            //开启动画设置滚动效果移动 
            element.timer = setInterval(function(){

                //1.每一次动画开启之前,默认设置isStop为true(定时器停止)
                //2.如果只是一个属性完成不需要修改定时器,如果有属性没有    执行完,则设置isStop = false,继续开启定时器
                //3.最后所有属性都完成后, 判断isStop值     如果为true,表示的属性均执行完成,关闭定时器
                //1.
                isStop = true;

                //多个属性  分别计算每个属性当前值(实际值)/目标值/步长
                //遍历json参数  分别获取key-属性名 json[key]-属性值
                for(var key in json){
                    console.log(key);        //left top
                    console.log(json[key]);  // 100 50

                    //通过getStyle函数获取当前属性(key)的属性值即盒子的当前实际值
                    var current = parseInt(getStyle(element, key));
                    //获取json参数传入的每个属性对应的目标值
                    var target = json[key];
                    //分别设置每个属性步长
                    var step = (target - current) / 10;
                    step = step > 0 ? Math.ceil(step) : Math.floor(step);

                    //设置一步步的改变 直至达到目标值
                    current += step;

                    //判断(current += step)是否达到目标值 停止计时器
                    //2.其中一个属性完成,就不需要修改定时器
                    if(Math.abs(target -current) > Math.abs(step)){
                        isStop = false;
                    }else{ //强制将此属性设到target
                        current = target;
                    }

                    //设置运动后的值给元素, 改变其对应属性的属性值
                    element.style[key] = current + 'px';    
                }

                //3.所有的属性动画完成(for(key)结束),所有的定时器都为true,关闭定时器
                if(isStop){
                    clearInterval(element.timer);
                    console.log('完成动画');
                }
            },30);
        }
        //调用此函数
        window.onload = function(){
            var box =document.getElementById('box');
            document.onclick =function(){
                //实现点击后,在一定时间内同时完成以下动作
                animate(box,{
                    left:200,
                    top :200,
                    width:300,
                    height:300
                });
            }
        }

实现效果:鼠标点击后,在一定时间内盒子同时向左向下移动200px,并且宽高扩大到300px。

5、函数的回归调用(上一个动画运动完成后下一个动画才开始运动)

上面的代码实现了一个物体的多个属性同时运动,很多情况下会是一个物体的上一个动画完成后另一个动画才开始运动。所以我们在原来的基础上传入一个函数参数,function animate(element,json,fun){},在上一个动画完成后,开始调用下一个动画的函数参数。

  //回调函数
        function animate(element,json,fun){
            clearInterval(element.timer);
            console.log(element.offsetLeft + 'kaishiqian')
            var isStop = false;

            element.timer = setInterval(function(){

                isStop = true;

                for(var key in json){
                    console.log(key);        //left top
                    console.log(json[key]);  // 100 50
                    var current = parseInt(getStyle(element, key));
                    var target = json[key];
                    var step = (target - current) / 10;
                    step = step > 0 ? Math.ceil(step) : Math.floor(step);
                    current += step;

                    if(Math.abs(target -current) > Math.abs(step)){
                        isStop = false;
                    }else{ //强制将此属性设到target
                        current = target;
                    }
                    element.style[key] = current + 'px';    
                }

                if(isStop){
                    clearInterval(element.timer);
                    console.log('完成动画');
                    console.log(element.offsetLeft);

                    //上一个动画完成后,开始下一个动画
                    if(typeof fun == 'function'){
                        fun();
                    }
                }
            },30);
        }

        window.onload = function(){
            var box =document.getElementById('box');
            document.onclick =function(){
                //先向右移动到500px,接着宽高均扩大到300px,下移到150px,字体放大到30px
                animate(box,{left:500}, function(){
                    animate(box,{width:300, height:300}, function(){
                        animate(box,{top:150}, function(){
                            animate(box,{fontSize:30}, null);
                        });
                    });
                });
            }
        }

实现效果:鼠标点击后,盒子先向右移动到500px,接着宽高均扩大到300px,下移到150px,字体放大到30px。

6、封装带有opacity、z-index等属性的动画函数
//思路:即查看current,target,step的值是否会因为opacity的传入而出错
        function animate(element,json,fun){
            clearInterval(element.timer);
            console.log(element.offsetLeft + 'kaishiqian')
            var isStop = false;

            element.timer = setInterval(function(){

                isStop = true;

                for(var key in json){

                    var current;
                    //如果传入的属性是opacity,取浮点型数
                    if(key == 'opacity'){
                        current = parseFloat(getStyle(element, key));
                    }else{
                        current = parseInt(getStyle(element, key));
                    }

                    //没问题
                    var target = json[key];

                    //只要不是opacity 都做向上或向下取整操作
                    var step = (target - current) / 10;
                    if(key != 'opacity'){
                        step = step > 0 ? Math.ceil(step) : Math.floor(step);
                    }

                    current += step;

                    //判断暂停动画
                    if(key == 'opacity'){

                        if(Math.abs(target -current) > 0.01){
                            isStop = false;
                        }else{ 
                            current = target;
                        }
                        element.style[key] = current + '';

                    }else{

                        if(Math.abs(target -current) > Math.abs(step)){
                            isStop = false;
                        }else{ //强制将此属性设到target
                            current = target;
                        }
                        
                        if(key == 'zIndex'){
                            //四舍五入
                            element.style.zIndex = Math.round(current);
                        }else{
                            element.style[key] = current + 'px';    
                        }
                        
                    }
                }
                if(isStop){
                    clearInterval(element.timer);
                    console.log('完成动画');
                    if(typeof fun == 'function'){
                        fun();
                    }
                }
            },30);
        }

        window.onload = function(){
            var box =document.getElementById('box');
            document.onclick =function(){

                animate(box, {opacity:0.3,zIndex:20}, null);
            }
        }

实现效果:透明度由1 变为0.3 ,z-index由10 变为20。

到这里一个较为完善的动画函数就封装完成了。

如有任何疑问请留言。

接下将介绍几种通过js动画效果实现各式轮播图的案例......

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,574评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,620评论 4 59
  • ˙Ꙫ˙ ଲ ̊ଳ ̊ ⸝⸝⸝⸝̆̆ ´͈ ᵕ `͈ ⚆_⚆ (-᷅_-᷄) ₍₍...
    邢月婷阅读 9,658评论 0 1
  • O:早上晨会,领导分享了柯达的故事: 柯达之前是行业的巨霸,由于胶卷利润率太可观等一些原因,高层把新技术隐藏起来,...
    海洋里的小彩鱼阅读 163评论 0 0
  • 感冒发烧鼻塞咳嗽简直就是这个季节的常态。虽然我们都知道这些问题不大,很快就会好;虽然我们都懂小朋友就是这样渐渐有了...
    小北北妈阅读 767评论 3 7