JavaScript封装减速运动框架

假设需要实现一个div 盒子的宽高减速运动变化过渡,首先要思考的问题就是,现在div盒子的宽高是多少?要变化到多少?

首先需要解决的问题,需要获取用户输入对象的当前css属性值,其次JavaScript要实现减速运动,运动变化到多少需要使用定时器来把运动拆分成不同时间段的具体变化,从而实现动画的过渡。下面来详细讲解JavaScript如何实现减速运动。

不同的浏览器获取css属性值的写法不一样,所以要书写判断。IE和Opera浏览器支持的写法是obj.currentStyle[attr],其他w3c支持的浏览器支持的写法是getComputedStyle(obj,null)[attr]。

以下是封装获取div元素的css属性值的代码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style>
            
            #box{width: 200px; height: 300px; background:tan; position: absolute; left: 0;}
                        
        </style>
    </head>
    <body>
        
        
        <button id="btn1">按钮1</button>
        <button id="btn2">按钮2</button>
        
        <div id="box"></div>
        
        <script>
            
            var btn1 = document.getElementById("btn1");
            var btn2 = document.getElementById("btn2");
            var box = document.getElementById("box");
            
            //封装获取用户输入对象对应css属性值函数
            function getStyle(obj,attr){
                //如果obj存在currentStyle
                if(obj.currentStyle){
                    //IE Opera浏览器
                    //返回对象属性值
                    return obj.currentStyle[attr];
                }else{
                    //w3c支持的浏览器
                    return getComputedStyle(obj,null)[attr];
                }
            }
            
            //这里输出盒子的高度300px
            //console.log(getStyle(box,"height"));
            
        </script>       
    </body>
</html>

现在已经可以得到用户输入属性对应的css属性值,下面来讲解如何实现动画。

首先需要理解减速运动的公式,减速运动在于每次的运动的步长越来越小,需要在循环定时器内计算每次运动的步长。

步长 = (目标值 - 当前值)/ 步数

在循环定时器内部,当前值是不断获取不断变化的,并且越来越接近目标值,而对象的css属性值也需要不断变化。

对象的css属性值 = 当前值 + 步长

目标值是用户输入的属性最终的值,而用户输入的最终值可能是多个属性,所以最终值应该是一个json数据,多个属性操作则需要对json数据进行遍历。当前值则需要在定时器内不断的获取。

实现代码和详细注释:

<script>
            
            var btn1 = document.getElementById("btn1");
            var btn2 = document.getElementById("btn2");
            var box = document.getElementById("box");
            
            //封装获取用户输入对象对应css属性值函数
            function animate(obj,json){

                setInterval(function(){
                    //遍历用户输入属性json数据
                    for(k in json){
                        //k是用户输入属性
                        //json[k]是对应属性的值
                        //console.log(json[k]);
                        //获取对象当前css属性值,获取的是字符型,需要转化成数字
                        var curStyle = parseInt(getStyle(obj,k));
                        
                        //得到步长
                        //步长 = (最终值 - 当前值)/ 步数;
                        var step = (json[k] - curStyle) / 10;
                        
                        //控制台输出能看到步长不断减小
                        //console.log(step)
                        
                        //对象的css属性值 = 当前值 + 步长;
                        obj.style[k] = curStyle + step + "px";
                        
                    }
                                
                },10)
                
            }
            
            //点击btn1能看到box宽高动画变化
            btn1.onclick = function(){
                animate(box,{width:400,height:600});    
            }

</script>

如下图所示,点击btn1已经可以实现盒子宽高的减速动画变化过渡。

减速动画变化

前面步骤虽然已经可以实现盒子动画减速过渡变化,但是审查元素发现盒子最终宽高并不等于用户输入的数值。如下图所示:

审查元素

原因是因为浏览器最小只能识别1px,但是步长step在除以步数的过程中,会出现除不尽的小数情况。所以在这里步长要作一个取整处理。如果步长是正数则向上取整,否则向下取整。

Math.ceil()向上取整,取比该数值大的整数
Math.floor()向下取整,取比该数值小的整数

步长加上以下判断赋值则可以解决这个问题。

step = step>0?Math.ceil(step):Math.floor(step);

上面已经可以实现盒子宽高减速运动变化,那么什么时候停止定时器呢?

所有用户输入的css属性都完成动画变化过渡则可以停止定时器。

在遍历外声明两个变量来记录属性变化,声明num记录属性总个数,声明key来记录已经完成变化属性的个数。遍历前两个变量的值都先设置为0。

属性总个数num遍历一次增加一个num++,已经完成变化属性个数key,变化完成一个增加一个 key++,怎么判断属性是否变化完成?如果当前值 = 目标值,该属性就变化完成。

下面代码把定时器赋给了对象obj的timer属性,如果需要调整运动速度可以通过修改定时器的间隔时间或者步数。

以下代码包含详细注释:

<script>
            
            var btn1 = document.getElementById("btn1");
            var btn2 = document.getElementById("btn2");
            var box = document.getElementById("box");
            
            //封装获取用户输入对象对应css属性值函数
            function getStyle(obj,attr){
                //如果obj存在currentStyle
                if(obj.currentStyle){
                    //IE Opera浏览器
                    //返回对象属性值
                    return obj.currentStyle[attr];
                }else{
                    //w3c支持的浏览器
                    return getComputedStyle(obj,null)[attr];
                }
            }
            
            //这里输出盒子的高度300px
            //console.log(getStyle(box,"height"));
            
            //封装缓动动画函数
            function animate(obj,json){
                
                //为了优化效率,开启定时器前先清除定时器
                clearInterval(obj.timer);
                
                obj.timer = setInterval(function(){
                    
                    //在遍历外记录属性个数,初始都定义为0
                    var num = 0;    //记录总个数
                    var key = 0;//记录已达到属性的个数
                    
                    //遍历用户输入属性json数据
                    for(k in json){
                        //k是用户输入属性
                        //json[k]是对应属性的值
                        //console.log(json[k]);
                        //获取对象当前css属性值,获取的是字符型,需要转化成数字
                        var curStyle = parseInt(getStyle(obj,k));
                        
                        //得到步长
                        //步长 = (最终值 - 当前值)/ 步数;
                        var step = (json[k] - curStyle) / 10;
                        
                        //控制台能看到步长不断减小
                        //console.log(step)
                        
                        //Math.ceil()向上取整,取比该数值大的整数
                        //Math.floor()向下取整,取比该数值小的整数
                        //步长判断取整后重新赋值
                        step = step>0?Math.ceil(step):Math.floor(step);
                        
                        //对象的css属性值 = 当前值 + 步长;
                        obj.style[k] = curStyle + step + "px";
                        
                        //属性总个数遍历一次增加一个
                        num++;
                        
                        //如果当前值等于用户输入的值,则已经完成一个属性的变化
                        if(curStyle == json[k]){
                            //key记录的是已经完成变化的属性数,到达一个增加一个key++
                            key ++;
                        }
                                        
                        
                    }
                                    
                    //在遍历外面判断到达属性的个数是否等于遍历属性的总个数                
                    if(key == num){
                        //如果两个相等,表示所有属性变化完成,则停止定时器
                        clearInterval(obj.timer)
                    }
                    
                    //console.log(num)
                    //console.log(key)          
                    
                },30)
                
            }
            
            //点击btn1能看到box属性动画变化
            btn1.onclick = function(){
                animate(box,{width:1200,height:400,marginTop:100}); 
            }
            
            //点击btn2能看到box属性动画变化
            btn2.onclick = function(){
                animate(box,{width:200,height:200,marginTop:0});    
            }

                        
        </script>   

点击按钮效果,如下图所示:

点击按钮效果

到这一步,已经完成了减速运动变化框架封装。

最后一步来进行优化,为减速运动框架添加回调函数。

所谓回调函数是指在动画运动完成后执行的函数。需要给animate函数添加一个参数fn接收回调函数,如果用户有传回调函数,运动完成后就执行这个函数,所以是在停止定时器后再进行判断和调用fn。

下面为减速运动框架封装最终代码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style>
            
            #box{width: 200px; height: 300px; background:tan; position: absolute; left: 0;}
                        
        </style>
    </head>
    <body>
        
        
        <button id="btn1">按钮1</button>
        <button id="btn2">按钮2</button>
        
        <div id="box"></div>
        
        <script>
            
            var btn1 = document.getElementById("btn1");
            var btn2 = document.getElementById("btn2");
            var box = document.getElementById("box");
            
            //封装获取用户输入对象对应css属性值函数
            function getStyle(obj,attr){
                //如果obj存在currentStyle
                if(obj.currentStyle){
                    //IE Opera浏览器
                    //返回对象属性值
                    return obj.currentStyle[attr];
                }else{
                    //w3c支持的浏览器
                    return getComputedStyle(obj,null)[attr];
                }
            }
            
            //这里输出盒子的高度300px
            //console.log(getStyle(box,"height"));
            
            //封装缓动动画函数
            function animate(obj,json,fn){
                
                //为了优化效率,开启定时器前先清除定时器
                clearInterval(obj.timer);
                
                obj.timer = setInterval(function(){
                    
                    //在遍历外记录属性个数,初始都定义为0
                    var num = 0;    //记录总个数
                    var key = 0;//记录已达到属性的个数
                    
                    //遍历用户输入属性json数据
                    for(k in json){
                        //k是用户输入属性
                        //json[k]是对应属性的值
                        //console.log(json[k]);
                        //获取对象当前css属性值,获取的是字符型,需要转化成数字
                        var curStyle = parseInt(getStyle(obj,k));
                        
                        //得到步长
                        //步长 = (最终值 - 当前值)/ 步数;
                        var step = (json[k] - curStyle) / 10;
                        
                        //控制台能看到步长不断减小
                        //console.log(step)
                        
                        //Math.ceil()向上取整,取比该数值大的整数
                        //Math.floor()向下取整,取比该数值小的整数
                        //步长判断取整后重新赋值
                        step = step>0?Math.ceil(step):Math.floor(step);
                        
                        //对象的css属性值 = 当前值 + 步长;
                        obj.style[k] = curStyle + step + "px";
                        
                        //属性总个数遍历一次增加一个
                        num++;
                        
                        //如果当前值等于用户输入的值,则已经完成一个属性的变化
                        if(curStyle == json[k]){
                            //key记录的是已经完成变化的属性数,到达一个增加一个key++
                            key ++;
                        }                                   
                        
                    }
                                    
                    //在遍历外面判断到达属性的个数是否等于遍历属性的总个数                
                    if(key == num){
                        //如果两个相等,表示所有属性变化完成,则停止定时器
                        clearInterval(obj.timer);
                        //如果存在就调用
                        //if(fn){
                            //fn();
                        //}
                        //化简写法
                        fn&&fn();
                    }
                    
                    //console.log(num)
                    //console.log(key)          
                    
                },20)
                
            }
            
            //点击btn1能看到box属性动画变化
            btn1.onclick = function(){
                animate(box,{width:1200,height:400,marginTop:100},function(){
                    alert("按钮1运动完成!")
                });
            }
            
            //点击btn2能看到box属性动画变化
            btn2.onclick = function(){
                animate(box,{width:200,height:200,marginTop:0});    
            }
                        
        </script>
        
    </body>
</html>

注意这个减速运动框架只适用于属性值为正整数的属性,属性值为小数例如opacity和属性值非数字的属性暂不支持。

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

推荐阅读更多精彩内容

  • HTML 5 HTML5概述 因特网上的信息是以网页的形式展示给用户的,因此网页是网络信息传递的载体。网页文件是用...
    阿啊阿吖丁阅读 3,888评论 0 0
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,486评论 1 45
  • 15、正则 正则就是一个规则,用来处理字符串的规则1、正则匹配编写一个规则,验证某个字符串是否符合这个规则,正则匹...
    萌妹撒阅读 1,444评论 0 1
  • JavaScript 将字符串转换为数字 parseInt() ◆只保留数字的整数部分,不会进行四舍五入运算。 ...
    AkaTBS阅读 985评论 0 9
  • JS基础 页面由三部分组成:html:超文本标记语言,负责页面结构css:层叠样式表,负责页面样式js:轻量级的脚...
    小贤笔记阅读 603评论 0 5