Js运动-完美运动

JavaScript之深入Js运动–搭建完美运动框架

上一篇文章向大家介绍并且实现了最基础的Js运动,包括元素的匀速运动、淡入淡出效果、缓冲运动和多物体运动。在这一篇文章中我们继续深入研究Js运动,最后为大家封装一个完美运动框架,这样我们就能拿着这个框架“胡作非为”!比如去编写一个banner轮播图效果,页面放烟花效果等等~

一、多物体多样式运动

接上篇,我们现在研究一下多物体多样式运动。什么是多物体多样式运动?比如说,我这里有5个相同的元素,我想封装一个startMove()函数让这5个元素做不同的事情:

  1. 让第一个元素的宽增加到300px;
  2. 让第二个元素的高增加100px;
  3. 让第三个元素向右缓冲运动;
  4. 让第四个元素字体放大;
  5. 改变第五个元素的透明度。

先上图看看效果!
多物体多样式效果图
为了实现上面的效果,那就继续改进改进我们的startMove()框架,这一次我们***把元素的css样式类型作为形参***传递进来,这样就可以任意修改元素样式的运动啦!

首先我们先获取到css样式,这里封装一个getStyle()函数,进行获取当前元素样式。node.style仅能获取到元素的内联样式,获取不到外部样式,所以我们先来学习两个用Js获取到元素的内联样式或者外部样式的方法:node.currentStyle和getComputedStyle()

node.currentStyle仅支持IE浏览器使用,getComputedStyle()支持非IE浏览器使用,所以这里我们要用if-else做一下兼容处理。

/*
    node  元素节点
    cssStyle  获取css样式类型
*/
function getStyle(node, cssStyle){
    if(node.currentStyle){
        return node.currentStyle[cssStyle];//使用js获取元素当前样式,仅兼容IE浏览器
    }else{
        return getComputedStyle(node)[cssStyle];//使用js获取元素当前样式,兼容非IE浏览器
    }
}

获取到元素样式,我们就能改进startMove()函数了:

//多物体多样式的运动
function startMove(node, attr, iTarget){
    clearInterval(node.timer);//关闭上一次定时器
    node.timer = setInterval(function(){
        //1、获取当前值
        var iCur = null;
		//透明度和其他样式的兼容处理
        if(attr == "opacity"){
            iCur = parseInt(parseFloat(getStyle(node, "opacity")) * 100);//封装的获取css样式的getStyle()函数用在这里。
        }else{
            iCur = parseInt(getStyle(node, attr))//封装的获取css样式的getStyle()函数用在这里。
        }
        //2、计算速度
        var speed = (iTarget - iCur) / 8;//最佳缓冲效果
        speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);//速度取整
        //3、运动和停止分开
        if(iCur == iTarget){
            clearInterval(node.timer);//到达目的值关闭定时器
        }else{
            if(attr == "opacity"){//如果修改样式是透明度
                iCur += speed;
                node.style.opacity = iCur / 100;
                node.style.filter = `alpha(opacity=${iCur})`;//透明度兼容写法
            }else{//修改样式不是透明度,就执行下面的方法。
                node.style[attr] = iCur + speed + 'px';
            }
        }
    }, 30);
}

这样我们就可以使用getStyle()函数和startMove()函数进行多物体多样式运动了。完整代码奉上:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            div{width: 100px; height: 50px; background-color: red; font-size: 20px; margin: 50px;}
            div.box{opacity: 0.3; filter: alpha(opacity=30);}
        </style>
        <script>
            window.onload = function(){
                var aDivs = document.getElementsByTagName("div");

                //点击第一个div,让宽100 => 300
                aDivs[0].onclick = function(){
                    startMove(this, "width", 300);
                }
                
                //高变成100 => 300
                aDivs[1].onclick = function(){
                    startMove(this, "height", 300);
                }
                
                //marginLeft 100 => 300
                aDivs[2].onclick = function(){
                    startMove(this, "marginLeft", 300);
                }
                
                //fontSize 20 => 100
                aDivs[3].onclick = function(){
                    startMove(this, "fontSize", 100);
                }
                
                //透明度的变化
                aDivs[4].onmouseover = function(){
                    startMove(this, "opacity", 100);
                }
                aDivs[4].onmouseout = function(){
                    startMove(this, "opacity", 30);
                }
            }
            
                //多物体多样式的运动   startMove(this, "width", 300);
                function startMove(node, attr, iTarget){
                    clearInterval(node.timer);
                    node.timer = setInterval(function(){
                        //1、获取当前值
                        var iCur = null;

                        if(attr == "opacity"){
                            iCur = parseInt(parseFloat(getStyle(node, "opacity")) * 100);
                        }else{
                            iCur = parseInt(getStyle(node, attr))
                        }
                        //2、计算速度
                        var speed = (iTarget - iCur) / 8;
                        speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
                        //3、运动和停止分开
                        if(iCur == iTarget){
                            clearInterval(node.timer);
                        }else{
                            if(attr == "opacity"){
                                iCur += speed;
                                node.style.opacity = iCur / 100;
                                node.style.filter = `alpha(opacity=${iCur})`;

                            }else{
                                node.style[attr] = iCur + speed + 'px';
                            }
                        }
                    }, 30);
                }
                /*
                    node  元素节点
                    cssStyle  获取css样式类型
                */
                function getStyle(node, cssStyle){
                    if(node.currentStyle){
                        return node.currentStyle[cssStyle];
                    }else{
                        return getComputedStyle(node)[cssStyle];
                    }
                }
        </script>
    </head>
    <body>
        <div></div>
        <div></div>
        <div></div>
        <div>div文本</div>
        <div class = 'box'></div>
    </body>
</html>

二、链式运动

了解完多物体多样式运动,大家来想一个问题:如何让一个元素进行多样式运动呢?

这里我们就要学习一下链式运动了,即上一个运动完成接着执行下一个运动。学习链式运动之前我们需要先来了解一下 回调函数的概念。回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

我个人理解的回调函数就是一个函数设置为另一个函数的参数。

举个栗子看一下:

扫描二维码关注公众号,回复: 9368186 查看本文章
<script>
/*
    1、先让你选择怎么吃 
        <1>堂食  <2>外带
    2、可以开始点餐
    3、取餐 做<1>堂食<2>外带
*/
//编写一个KFC点餐系统
function xiaoming(){
    // document.write("小明要了一杯免费的水,吃起了自己喜欢的香辣鸡腿堡");
}
var xiaohua = function(){
    // document.write("小花选择了外带,选择了圣代,希望带回去和弟弟一起享用");
}
function KFC(food, eatFunc){ //eatFunc = xiaoming
    document.write("您的" + food + "已经准备好了,请您及时取餐。");
    eatFunc(); //执行吃的方式
}            
KFC("香辣鸡腿堡", xiaoming);
document.write("<br>")
// KFC("两个圣代", xiaohua);
KFC("两个圣代", function(){
    document.write("小花选择了外带,选择了圣代,希望带回去和弟弟一起享用");
})
</script>

输出结果
使用回调函数
了解完回调函数,我们来看看链式运动,在这里给startMove()函数设置一个叫complete的参数(complete相当于即以后要传进来的函数)。

来看完整版代码,学习链式运动。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            #div1{width: 100px; height: 100px; background-color: red;}
        </style>
        <script>
            window.onload = function(){
                var oDiv = document.getElementById("div1");
                /*
                    【注】每一个动画都开始在上一个动画结束的时候。
                */
                //链式运动 宽100=>300 然后高100=>300 透明度100=>30
                oDiv.onmouseover = function(){
                    startMove(this, "width", 300, function(){
                        startMove(this, "height", 300, function(){
                            startMove(this, "opacity", 30);
                        })
                    });                  
                }
                //链式运动 透明度30=>100  然后高300=>100 宽300=100
                oDiv.onmouseout = function(){
                    startMove(this, "opacity", 100, function(){
                        startMove(this, "height", 100, function(){
                            startMove(this, "width", 100);
                        })
                    });
                }
            }
        </script>

        <script>
            //多物体多样式的运动   startMove(this, "width", 300);
            function startMove(node, attr, iTarget, complete){ //complete = show;
                clearInterval(node.timer);
                node.timer = setInterval(function(){
                    //1、获取当前值
                    var iCur = null;
					//样式
                    if(attr == "opacity"){
                        iCur = parseInt(parseFloat(getStyle(node, "opacity")) * 100);
                    }else{
                        iCur = parseInt(getStyle(node, attr))
                    }
                    //2、计算速度
                    var speed = (iTarget - iCur) / 8;
                    speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
                    
                    //3、运动和停止分开
                    if(iCur == iTarget){
                        clearInterval(node.timer);
                        //这里是上一个动画结束的时候,但是动画结束以后,我们要干什么是不确定的
                        //一般情况下我们把函数中的不确定值声明成形参,一大段代码不确定,也可以声明成形参
                        //回调函数:这种直接将函数当做参数的写法,叫做回调函数。
                        if(complete){
                            complete.call(node); //执行函数,强制改变this指向
                        }
                        
                    }else{
                    	//判断是否是透明度
                        if(attr == "opacity"){
                            iCur += speed;
                            node.style.opacity = iCur / 100;
                            node.style.filter = `alpha(opacity=${iCur})`;
                        }else{//样式不是透明度就执行下面的方法
                            node.style[attr] = iCur + speed + 'px';
                        }
                    }
                }, 30);
            }
            // 获取css样式
            function getStyle(node, cssStyle){
                if(node.currentStyle){
                    return node.currentStyle[cssStyle];//获取css样式,IE浏览器兼容
                }else{
                    return getComputedStyle(node)[cssStyle];//获取css样式,非IE浏览器兼容
                }
            }
        </script>
    </head>
    <body>
        <div id = 'div1'></div>
    </body>
</html>

链式运动效果图:
链式运动

三、封装完美运动函数

研究完以上内容,我们终于到了本篇文章的重头戏了 => 封装一个完美运动函数。

虽然使用链式运动已经能够达到比较完美的效果,但是在书写的时候需要一直使用回调函数调用函数实现运动效果。

我们改进一下,使用 对象 的方法实现完美运动效果。

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            #div1{width: 100px; height: 100px; background-color: black; color: white;}
        </style>
        <script>  
            window.onload = function(){
                var oDiv = document.getElementById("div1");
                //将需要到达的运动值设置成对象
                //鼠标移入效果
                oDiv.onmouseover = function(){
                    startMove(this, {
                    	//宽 100px => 300px
                        width: 300,
                        //高 100px => 200px
                        height: 200,
                        //透明度 100 => 30
                        opacity: 30
                    }, function(){
                        this.innerHTML = "移入动画结束";//判断移入动画是否结束
                    });
                }
                oDiv.onmouseout = function(){
                    startMove(this, {
                    	//宽 300px => 100px
                        width: 100,
                        //高 200px => 100px
                        height: 100,
                        //透明度 30 => 100
                        opacity: 100
                    }, function(){
                        this.innerHTML = "移出动画结束";//判断移出动画是否结束
                    });
                }
            }
        </script>
        <script>
        	//这里将attr改成了cssObj
            function startMove(node, cssObj, complete){ //complete = show;
                clearInterval(node.timer);//关闭上一次定时器
                /*
                需求:想要实现多个css的变化
                【注】问题:当其中有一个动画到达目的值,整体就必须关闭掉定时器。
                     解决:定时器一定是等所有的动画都到达目的值以后才能关闭定时器。声明一个是否到达目的值的变量。
            	*/
                node.timer = setInterval(function(){         
                    var isEnd = true; //假设所有动画都到达了目的值
                    for(var attr in cssObj){
                        //取出当前css样式的目的值
                        var iTarget = cssObj[attr];
                        //1、获取当前值
                        var iCur = null;
                        //判断是否进行透明度运动
                        if(attr == "opacity"){
                        	//计算透明度的时候,如果遇到这种有不兼容的问题,直接通过中间变量完成计算
                            iCur = parseInt(parseFloat(getStyle(node, "opacity")) * 100);
                        }else{
                        	//不是透明度运动将执行下面的运动方式
                            iCur = parseInt(getStyle(node, attr))
                        }
                        //2、计算速度
                        var speed = (iTarget - iCur) / 8;//完美的缓冲运动速度
                        speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);//速度值不能带小数,进行取整操作
						//判断是否是透明度操作
                        if(attr == "opacity"){
                            iCur += speed;
                            node.style.opacity = iCur / 100;
                            node.style.filter = `alpha(opacity=${iCur})`;
                        }else{
                            node.style[attr] = iCur + speed + 'px';
                        }
                        //当前值是否到达目的值
                        if(iCur != iTarget){
                            isEnd = false;//当前值不等于目的值,就不关闭定时器
                        }
                    }
                    //判断是否到达目的值
                    if(isEnd){
                        //说明都到达目的值,关闭定时器
                        clearInterval(node.timer);
                        //执行下一个运动
                        if(complete){
                            complete.call(node);
                        }
                    }
                }, 30); //30ms运动一下
            }
            /*
            	获取css样式操作
                node  元素节点
                cssStyle  获取css样式类型
            */
            function getStyle(node, cssStyle){
                if(node.currentStyle){
                    return node.currentStyle[cssStyle];
                }else{
                    return getComputedStyle(node)[cssStyle];
                }
            }
        </script>
    </head>
    <body>
        <div id = 'div1'>11</div>
    </body>
</html>

完美运动效果图
完美运动
完美运动函数已经给大家封装完成,大家可以直接拿去实现轮播图,分享到菜单,贪吃蛇,放烟花等等运动效果了。不过大家要学会如何调用完美运动函数,看看上面的案例,动动脑子自己研究一下,相信那么聪明的你一看就会了!

最后,把已经封装好的startMove()函数直接丢给大家,大家接住!

function startMove(node, cssObj, complete){ //complete = show;
    clearInterval(node.timer);//关闭上一次定时器
    /*
    需求:想要实现多个css的变化
    【注】问题:当其中有一个动画到达目的值,整体就必须关闭掉定时器。
         解决:定时器一定是等所有的动画都到达目的值以后才能关闭定时器。声明一个是否到达目的值的变量。
    */
    node.timer = setInterval(function(){         
        var isEnd = true; //假设所有动画都到达了目的值
        for(var attr in cssObj){
            //取出当前css样式的目的值
            var iTarget = cssObj[attr];
            //1、获取当前值
            var iCur = null;
            //判断是否进行透明度运动
            if(attr == "opacity"){
                //计算透明度的时候,如果遇到这种有不兼容的问题,直接通过中间变量完成计算
                iCur = parseInt(parseFloat(getStyle(node, "opacity")) * 100);
            }else{
                //不是透明度运动将执行下面的运动方式
                iCur = parseInt(getStyle(node, attr))
            }
            //2、计算速度
            var speed = (iTarget - iCur) / 8;//完美的缓冲运动速度
            speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);//速度值不能带小数,进行取整操作
            //判断是否是透明度操作
            if(attr == "opacity"){
                iCur += speed;
                node.style.opacity = iCur / 100;
                node.style.filter = `alpha(opacity=${iCur})`;
            }else{
                node.style[attr] = iCur + speed + 'px';
            }
            //当前值是否到达目的值
            if(iCur != iTarget){
                isEnd = false;//当前值不等于目的值,就不关闭定时器
            }
        }
        //判断是否到达目的值
        if(isEnd){
            //说明都到达目的值,关闭定时器
            clearInterval(node.timer);
            //执行下一个运动
            if(complete){
                complete.call(node);
            }
        }
    }, 30); //30ms运动一下
}
/*
    获取css样式操作
    node  元素节点
    cssStyle  获取css样式类型
*/
function getStyle(node, cssStyle){
    if(node.currentStyle){
        return node.currentStyle[cssStyle];
    }else{
        return getComputedStyle(node)[cssStyle];
    }
}

看完本篇文章能Get到知识的小伙伴们,点个赞再走吧~~
顺便再给小编点点关注!

发布了2 篇原创文章 · 获赞 16 · 访问量 1373

猜你喜欢

转载自blog.csdn.net/weixin_44928961/article/details/104469442