CSS 动画专题(transition,animation,transform)

84 篇文章 3 订阅

1.目标及知识点:

  1. css3 transition 使用
  2. css3 animation 使用
  3. css3 transform 使用
- 利用CSS3 transition 实现动画 
  - transition 语法详解
  - transition-delay
  - transition-duration
  - transition-property
  - transition-timing-function
    - 贝塞尔曲线运动 https://cubic-bezier.com/
  - transition 针对为渲染元素的问题 
  - transitionend 事件
- 利用CSS3 animation 动画
  - keyframes
  - animation-name
  - animation-duration
  - animation-timing-function,
  - animation-delay
  - animation-iteration-count
  - animation-direction 
  - animation-fill-mode
  - animation 相关事件
    - animationstart
    - animationiteration 
    - animationend 
- CSS3 transform 详解
  - transform 2D
    - 旋转: rotate()
    - 缩放: scale()、scaleX()、scaleY()
    - 倾斜: skew()、skewX()、skewY()
    - 平移:translate()、translateX()、translateY()
    - transform 多函数书写时的执行顺序
    - transform-origin 源点设置
    - translate 和 源点关系
  - transform 3D 
    - 3D旋转: rotateX()、rotateY()、rotateZ()
    - 3D位移:translateZ()
    - transform-style
    - perspective
    - perspective-origin
    - 搭建立方体
- CSS3 transform 详解
  - transform 2D
    - 旋转: rotate()
    - 缩放: scale()、scaleX()、scaleY()
    - 倾斜: skew()、skewX()、skewY()
    - 位移:translate()、translateX()、translateY()
    - transform 多函数书写时的执行顺序
    - transform-origin 源点设置
    - translate 和 源点关系
    - 实例:无缝滚动
    - 实例:水滴按钮
    - 实例:时钟实现
    - 扩展:JS 获取 transform 的问题
      - matrix(a,b,c,d,e,f) 矩阵函数
	    - matrix(1,0,0,1,0,0);
      - 通过矩阵实现缩放
        - x轴缩放 a=x*a    c=x*c     e=x*e;
        - y轴缩放 b=y*b   d=y*d     f=y*f;
      - 通过矩阵实现位移
        - x轴位移: e=e+x
        - y轴位移: f=f+y
      - 通过矩阵实现倾斜
        - x轴倾斜: c=Math.tan(xDeg/180*Math.PI)
        - y轴倾斜: b=Math.tan(yDeg/180*Math.PI)
      - 通过矩阵实现旋转
        - a=Math.cos(deg/180*Math.PI); 
        - b=Math.sin(deg/180*Math.PI);
        - c=-Math.sin(deg/180*Math.PI);
		    - d=Math.cos(deg/180*Math.PI);
  - transform 3D 
    - 3D旋转: rotateX()、rotateY()、rotateZ()
    - 3D位移:translateZ()
    - transform-style
    - perspective
    - perspective-origin
    - 搭建立方体

2.transition

2.1 transition作用及样式

transition作用:

transition 在元素的样式发生改变时,给元素添加一个过渡动画。不是所有样式都有效果,必须是宽高,背景色,距离,位置等数值类样式才有效果。通过css和JS改变都会有动画效果。

transition样式

  1. transition-delay:延迟时间,动画延迟多长时间执行(s|ms),默认为0。可选
  2. transition-duration:动画时长,动画用多长时间完成(s|ms),默认为0,必填
  3. transition-property:要动画的样式。默认all。只给某些加样式如,transition:1s width;只给宽加过渡动画
  4. transition-timing-function:动画形式

transition-timing-function动画形式:

  • linear 匀速
  • ease 缓冲(默认值)
  • ease-in 加速
  • ease-out 减速
  • ease-in-out 先加速再减速
  • cubic-bezier()贝塞尔曲线

各个样式之间不加逗号。除非给宽高分别加样式:如,transition:1s width,.5s height;

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        div {
            width: 100px;
            height: 100px;
            background: red;
        }
        .hover {
            width: 400px;
            height: 200px;
            background: blue;
            /* transition: .5s;transition-delay动画延迟多长时间执行 */
            /* transition: .5s 1s; transition-duration动画用多长时间完成 */
            /* transition: .5s 1s width; transition-property:要动画的样式,默认all是给所有样式加过渡效果*/
            /* transition: 1s .5s width ease-in-out;  */
            /*
            transition-timing-function:动画形式:
                linear 匀速
                ease 缓冲(默认值)
                ease-in 加速
                ease-out 减速
                ease-in-out 先加速再减速
                cubic-bezier()贝塞尔曲线 https://cubic-bezier.com/
            */
            transition: 1s .5s width cubic-bezier(0,1.42,.83,.67);
        }
    </style>
</head>
<body>
    <div></div>
    <script>
        //设置鼠标移入移出事件,然后加上transition效果
        var div = document.querySelector("div");
        div.onmouseover = function(){
            this.classList.add("hover");
        };
        div.onmouseout = function(){
            this.classList.remove("hover");
        };
    </script>
</body>
</html>

2.2 transition使用时注意点

默认隐藏的元素,设置点击后再显示时,过渡动画会失效。

解决:定时器延时过渡动画执行

需求:默认div隐藏,点击按钮后再显示并设置过渡后效果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #box {
            width: 100px;
            height: 100px;
            background: red;
            display: none;
            transition: .5s;
        }
    </style>
</head>
<body>
    <div id="box"></div>
    <button>显示</button>
    <script>
        //元素本身是隐藏的,点击时设置block,并改变其宽度,设置动画过渡效果
        //延时最好不低于20:因为屏幕有渲染帧频问题,1s渲染60次,16.7ms渲染一次。如果写0可能卡在上一屏,动画过渡效果就可能出不来
        var box = document.querySelector("#box");
        var btn = document.querySelector("button");
        btn.onclick = function(){
            box.style["display"] = "block";
            box.style["width"] = "400px";
            box.style["background"] = "blue";
            // setTimeout(function(){
            //     box.style["width"] = "400px";
            //     box.style["background"] = "blue";
            // },20);
        };
    </script>
</body>
</html>

问题:发现此时并没有动画过渡效果。

原因:元素本身设置display为none是不会渲染这个元素的,当display设置为block时,才开始渲染这个元素。而元素在页面渲染完(display设置为block)之前,transition是不起效果的。即屏幕绘制(display设置为block)需要时间,当display设置为block时,这个元素渲染需要时间,而代码之间执行的速度很快,所以会直接执行下面设置的宽高等样式,而不会通过transition动画去执行,所以,动画效果没有执行,屏幕已经渲染完了。

解决:需要在事件里,给元素的改变加上定时器延时。时间至少20ms

            setTimeout(function(){
                box.style["width"] = "400px";
                box.style["background"] = "blue";
            },20);

2.3 监听transition执行结束的事件:

  • transitionend事件:监听元素的transition动画是否执行完毕
  • WebKitTransitionEnd:低版本WebKit内核需要写成WebKitTransitionEnd

直接使用监听事件ontransitionend会监听不到transition执行结束:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #box {
            width: 100px;
            height: 100px;
            background: red;
            display: none;
            
            transition: .5s;
        }
    </style>
</head>
<body>
    <div id="box"></div>
    <button>显示</button>
    <script>
        var box = document.querySelector("#box");
        var btn = document.querySelector("button");

        //如果元素事先隐藏,会监听到两次
         btn.onclick = function(){
            box.style["display"] = "block";
            setTimeout(function(){
                box.style["width"] = "400px";
                box.style["background"] = "blue";
            },20);
        };
        //监听事件ontransitionend方法没有效果
        btn.ontransitionend = function(){
            console.log("ontransitionend执行完成");//不会进行打印(没有监听到)
        };
        //监听transition执行结束事件transitionend
        box.addEventListener('transitionend',function(){
            console.log("addEventListener transitionend执行完成");
        });
    </script>
</body>
</html>

结果:ontransitionend没有监听并打印结果,通过事件监听正确监听到结果

注意:每个样式都是独立的过渡效果,而不是整体进行一次性进行过渡,所以设置了多个过渡效果,事件监听也就会监听到多少次,所以这里打印了两次。

2.4 取消监听事件

注意:

  • 如果要取消监听事件,则添加监听事件时不能使用匿名函数,而需要写成有名函数
  • 取消监听事件必须写在有名函数函数体内
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #box {
            width: 100px;
            height: 100px;
            background: red;
            
            transition: .5s;
        }
    </style>
</head>
<body>
    <div id="box"></div>
    <script>
        var box = document.querySelector("#box");
        //给box添加click监听事件,并给box width+200,然后监听结束后,取消监听
        box.addEventListener('click',fn);
        boxWidth = parseInt(getComputedStyle(box)["width"]);
        
        function fn(){
            boxWidth += boxWidth;
            box.style["width"] = boxWidth + 'px';
            //监听事件结束后,取消监听事件(注意必须写在监听的函数中)
            box.removeEventListener('click',fn);
        }
    </script>
</body>
</html>

2.4 使用监听事件好处

通过onXXX写事件时,多个onXXX事件前一个事件处理会被覆盖。但是通过添加事件监听方式,可以添加多个事件处理。

        var box = document.querySelector("#box");
        //给box添加click监听事件,并给box width+200,然后监听结束后,取消监听
        box.addEventListener('click',fn);
        box.addEventListener('click',function(){
            console.log("click事件2");
            
        });
        boxWidth = parseInt(getComputedStyle(box)["width"]);
        
        function fn(){
            boxWidth += boxWidth;
            box.style["width"] = boxWidth + 'px';
            //监听事件结束后,取消监听事件(注意必须写在监听的函数中)
            box.removeEventListener('click',fn);
        }

2.animation

2.1 transition作用及样式

transition作用及样式:

 transition只能做一些简单的动画,如果要做一些复杂的动画就得使用animation。而且如果希望元素一开始就有动画,就要使用animation。animation使用时必须声明关键帧@keyframes 名字

  • animation-name:动画帧名称 使用@keyframes 名字  进行定义 必选
  • animation-duration :动画持续时间 必选
  • animation-timing-function : 动画形式(参考transition)
  • animation-delay : 动画开始前的延迟时间
  • animation-iteration-count 动画执行次数 number | infinite(无限次)
  • animation-direction : 偶数次动画执行过程 alternate(倒序执行) | normal (顺序执行)
  • animation-fill-mode : backwards动画开始前元素的样式(保留在动画帧0%);forwards动画结束后,元素样式保留在动画帧0的100%的位置;both 动画开始前和结束后样式保留在动画帧0%和100%的位置,即backwards+forwards
  • animation-play-state : 鼠标移入后hover,动画暂停(paused)| 继续播放(running)

    #box:hover {

    animation-play-state:paused;

    }

animation使用注意点:

  • animation是基于关键帧@keyframes 名字实现;
  • 如果0%不写就默认使用计算后样式,可以对其进行重新定义;
  • 如果中间xx%不写就没有对应的过渡效果;
  • 如果100%不写就默认使用计算后样式,可以对其进行重新定义;
  • 动画执行完后,又会回到计算后样式
  • animation-fill-mode就能改变动画执行完前/后保留的样式。而不是默认0%/100%
  • 如果使用from{} to{} to{}会自动计算百分比进行渲染;
  • 关键帧声明好后,需要进行调用animation: move 1s;
  • 兼容老版本Chrome,必须加前缀-webkit-animation
  • animation-play-state必须用在鼠标移入事件中,否则会导致整个动画暂停

简单示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        /* animation是基于动画帧实现 */
        @keyframes move{
            /* 如果0%不写就默认使用计算后样式,可以对其进行重新定义 */
            0%{
                width: 0;
                height: 0;
            }
            25%{
                width: 100px;
                height: 150px;
            }
            50%{
                width: 150px;
                height: 300px;
            }
            75%{
                width: 300px;
                height: 100px;
            }
             /* 如果100%不写就默认使用计算后样式,可以对其进行重新定义,但是动画执行完后,又会回到计算后样式 */
            100%{
                width: 0;
                height: 0;
            }
            /* 如果使用from{}  to{} to{}会自动计算百分比进行渲染 */
        }

        #box {
            width: 100px;
            height: 100px;
            background: red;
            /* 动画帧的调用 */
            /* animation: move 1s infinite; 执行无限次*/
            /* animation: move 1s 4; 执行指定次数 */
            /* animation: move 1s infinite alternate; 倒序执行(必须多次执行才有效) */
            /* animation: move 1s infinite normal; 顺序执行 */
            animation: move 1s linear; 
            animation-fill-mode: both;/* 动画开始和结束后,元素样式保留在动画帧的0%和100% */
        }
        #box:hover {
            animation-play-state:paused;
        }
    </style>
</head>
<body>
    <div id="box"></div>
</body>
</html>

from{} to{} to{} 实现动画效果:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        @keyframes move{
            from {
                width: 100px;
                height: 100px;
            }
            to {
                width: 100px;
                height: 100px;
            }
            to {
                width: 200px;
                height: 100px;
            }
            to {
                width: 300px;
                height: 100px;
            }
            to {
                width: 400px;
                height: 100px;
            }
        }

        #box {
            width: 100px;
            height: 100px;
            background: red;
            animation: move 2s 4;
            animation-fill-mode: both; 
        }
        #box:hover {
            animation-play-state:paused;
        }
    </style>
</head>
<body>
    <div id="box"></div>
</body>
</html>

2.2 animation三个事件监听

  • animationstart(动画开始:第一次执行时监听到)
  • animationend(动画结束)
  • animationiteration(动画多次执行时使用,监听动画再次执行)

必须使用事件监听加这三个事件,而不能直接使用onXXX;

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        /* animation是基于动画帧实现 */
        @keyframes move{
            0%{
                width: 0;
                height: 0;
            }
            25%{
                width: 150px;
                height: 100px;
            }
            50%{
                width: 300px;
                height: 100px;
            }
            75%{
                width: 150px;
                height: 100px;
            }
            100%{
                width: 0;
                height: 0;
            }
        }

        #box {
            width: 100px;
            height: 100px;
            background: red;
            animation: move 2s linear 6; 
            animation-fill-mode: both;
        }
        #box:hover {
            animation-play-state:paused;
        }
    </style>
</head>
<body>
    <div id="box"></div>
    <script>
        var box = document.querySelector("#box");
        box.addEventListener("animationstart",function(){
            console.log("动画开始了");
        });
        box.addEventListener("animationend",function(){
            console.log("动画结束了");
        });
        box.addEventListener("animationstart",function(){
            console.log("动画又开始了");
        });
    </script>
</body>
</html>

结果:

 3.transform 2D

transform需要和transition或者animation一起使用才有过渡效果。

transform本身不脱离文档流,进行任何改变对其他元素都没有任何影响。

transform变换:

  1. 旋转:rotate()  单位:deg(角度,可为正负)。也有弧度,旋转几圈等单位,但用得比较少。
  2. 斜切:三个方法skew(x,y) skewX() skewY()  单位:deg(角度)
  3. 缩放:三个方法scale(x,y) scaleX() scaleY()  本身没有单位,只有倍数
  4. 位移:三个方法translate(x,y) translateX() translateY(),单位像素或其他

transform为了方便我们使用,将其所有的功能已经封装成对应的方法。

3.1 transform旋转rotate(deg):

transition实现transform的旋转

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #wrap {
            width: 600px;
            height: 600px;
            border: 1px solid black;
            margin: 0 auto;
        }
        #box {
            width: 200px;
            height: 200px;
            background: red;
            margin: 200px auto;
            font: 140px/200px "宋体";
            text-align: center;
            color: white;
            transition: 3s;
        }
        #wrap:hover #box{
            /* 旋转totate */
            transform: rotate(-360deg);
        }
    </style>
</head>
<body>
    <div id="wrap">
        <div id="box">上</div>
    </div>
</body>
</html>

animation实现旋转:

如果要设置一直旋转或其他操作就使用animation。把rotate()方法写在关键帧中。

animation实现旋转需要把transform旋转写在关键帧,再在对应元素样式中调用animation。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        /* animation实现旋转就不能写在hover样式里,就需要把transform旋转写在关键帧,在关键帧中写旋转方法,再在对应样式中调用animation */
        @keyframes rotate{
            from {
                transform: rotate(180deg);
            }
            to {
                transform: rotate(360deg);
            }
        }
        #wrap {
            width: 600px;
            height: 600px;
            border: 1px solid black;
            margin: 0 auto;
        }
        #box {
            width: 200px;
            height: 200px;
            background: red;
            margin: 200px auto;
            font: 140px/200px "宋体";
            text-align: center;
            color: white;
            animation: 3s rotate infinite;
        }
    </style>
</head>
<body>
    <div id="wrap">
        <div id="box">上</div>
    </div>
</body>
</html>

3.2 transform斜切:

transform斜切有三个方法:

  1. skew(x,y) : X轴和Y轴都进行拉伸
  2. skewX() : 沿着X轴方向拉,使其与Y轴形成对应夹角。
  3. skewY() : 沿着Y轴方向拉,使其与X轴形成对应夹角。

shewX():  角度为正数,沿着X轴方向拉两个对角(左上角和右下角),使其与Y轴形成对应角度的夹角;如果角度为负值,拉伸对角相反,沿着X轴方向拉两个对角(右上角和左下角),使其与Y轴形成对应角度的夹角;

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #wrap {
            width: 600px;
            height: 600px;
            border: 1px solid black;
            margin: 0 auto;
        }
        #box {
            width: 200px;
            height: 200px;
            background: red;
            margin: 200px auto;
            font: 140px/200px "宋体";
            text-align: center;
            color: white;
            transition: 3s;
        }
        #wrap:hover #box{
            /* 斜切skewX() */
            transform: skewX(30deg);
        }
    </style>
</head>
<body>
    <div id="wrap">
        <div id="box">上</div>
    </div>
</body>
</html>

 

shewY():  角度为正数,沿着Y轴方向拉两个对角(左上角和右下角),使其与X轴形成对应角度的夹角;如果角度为负值,拉伸对角相反,沿着Y轴方向拉两个对角(右上角和左下角),使其与X轴形成对应角度的夹角;

/* 斜切skewY() */

transform: skewY(30deg);

 

 

skew(x,y) :X轴和Y轴都进行拉伸

/* 斜切skew() */

transform: skew(-40deg,-30deg);

注意:skew(0)和skew(180)位置不一样。 skew(0)不会变化,skew(180)斜切180度。

3.3 transform 缩放:

transform缩放三个方法:transform缩放本身没有单位,只有倍数

  1. scale(x,y) : X,Y 轴都缩放
  2. scaleX() :X轴缩放
  3. scaleY()  :Y轴缩放

scale(x,y) :

            /* scale(x,y)缩放X 和 Y轴 */
            transform: scale(.5); 

 scaleX():

            /* scaleX()缩放X轴 */
            transform: scaleX(.5); 

 scaleY():

            /* scaleY()缩放Y轴 */
            transform: scaleY(.5);

3.4 transform 位移:

translate位移三个方法,单位像素或其他

  1. translate(x,y):沿x和y轴定位方向位移
  2. translateX() :沿X轴方向位移
  3. translateY() :沿Y轴方向位移

translate(x,y):

            /* translate(x,y)向X 和 Y轴坐标方向位移 */
            transform: translate(100px,200px); 

translateX():

            /* translateX()X轴方向位移 */
            transform: translateX(200px);

translateY():

            /* translateY()缩放Y轴方向位移 */
            transform: translateY(200px);

3.4 transform多函数书写时的执行顺序问题

transform中写多个函数(缩放,斜切,位移,旋转同时写)时,后写先计算

原因:CSS本身的解读顺序有关,CSS本身解读顺序就是从右向左进行解读。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #wrap {
            width: 600px;
            height: 600px;
            border: 1px solid black;
            margin: 0 auto;
        }
        #box1 {
            width: 200px;
            height: 200px;
            background: red;
            margin: 50px auto;
            font: 140px/200px "宋体";
            text-align: center;
            color: white;
            transition: 1s;
        }
        #box2 {
            width: 200px;
            height: 200px;
            background: blue;
            margin: 0 auto;
            font: 140px/200px "宋体";
            text-align: center;
            color: white;
            transition: 1s;
        }
        #wrap:hover #box1{
            transform: translateX(200px) scale(0.5);
            /* transform: translateY(200px) scale(0.5);  */
        }
        #wrap:hover #box2{
            transform: scale(0.5) translateX(200px);
            /* transform: scale(0.5) translateY(200px); */
        }
    </style>
</head>
<body>
    <div id="wrap">
        <div id="box1">上</div>
        <div id="box2">下</div>
    </div>
</body>
</html>

解析: 都是进行transform: scale(0.5) translateX(200px);缩放和X轴位移,但是发现box2的X轴位置和box1不一样,因为box1会先缩放0.5倍,再位移200px,而box2会先位移200px,再缩放0.5倍,所以整体box2比box1X轴方向少100px。

3.5 transform源点(锚点)transform-origin

transform源点:默认旋转、缩放、斜切都是围绕着元素的中心点进行变换。这是由于transform-origin变换基点的默认值决定。

transform-origin变换基点(旋转、缩放、斜切 围绕的哪个点进行操作):

  1. transform-origin默认值center center,即元素的正中心;
  2. 0,0点在元素的左上角;
  3. 其他自定义设置的点,在元素的具体对应位置。如transform-origin: 400px 400px;

位移本身围绕自己设置的轴进行位移所以没有源点的概念。

注意transform-origin: 400px 400px;设置在需要进行对应操作的元素上。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #wrap {
            width: 600px;
            height: 600px;
            border: 1px solid black;
            margin: 0 auto;
        }
        #box {
            width: 200px;
            height: 200px;
            background: red;
            margin: 200px auto;
            font: 140px/200px "宋体";
            text-align: center;
            color: white;
            transition: 1s;
            transform-origin: 400px 400px;
        }
        #wrap:hover #box{
            transform: scale(.5) rotate(360deg);
        }
    </style>
</head>
<body>
    <div id="wrap">
        <div id="box">上</div>
    </div>
</body>
</html>

3.6 transform应用一(时钟表盘)

纯CSS实现,使用图片,使用canvas位图三种实现方式,canvas位图方式相对性能最高。

思路:这里使用css实现

  1. 通过CSS画出表盘的刻度样式:li的源点为表盘的中心,没隔5个样式不一样li:nth-of-type(5n+1);
  2. 通过JS生成60个li;
  3. 设置时分秒指针,并进行定位,设置变换基点围绕底部进行旋转,设置中心点(为美观);
  4. 获取时分秒的时间,一秒钟秒钟指针走一个li(6度),一分钟分钟指针也走一个li(6度),一小时小时指针走5个li即30度
  5. 通过定时器,控制各个指针走动;
  6. 需要给时针和分针获取到小数位的小时或分钟,否则一小时会跳一整格;
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>时钟表盘</title>
    <style>
        ul,ul li {
            margin: 0;
            padding: 0;
            list-style: none;
        }
        #time {
            width: 200px;
            height: 200px;
            margin: 100px auto;
            border: 4px solid rgb(196, 13, 13);
            position: relative;
            border-radius: 50%;
        }
        #time li {
            width: 4px;
            height: 10px;
            background: rgba(255, 0, 0, 0.637);
            position: absolute;
            left: 98px;
            top: 0px;
            /* 源点:是以本元素为基点进行旋转 */
            transform-origin: 2px 100px;
        }
        /* 指定了父级元素下是 5 的倍数的第一个 li 背景色 */
        #time li:nth-of-type(5n+1){
            background: rgba(160, 20, 20, 0.863);
            height: 12px;
        }
        /* 给li设置的旋转样式 */
        .time_rotate {
            transform: rotate(0deg);
        }
        /* 2.设置时分秒针样式 */
        .hour {
            width: 6px;
            height: 50px;
            background: #000000;
            position: absolute;
            top: 50px;
            left: 97px;
            /* 设置上面两个角圆角时针 */
            border-radius:10px 10px 0 0 /50px;
            /* 设置所有角圆角 */
            /* border-radius:10/50px; 
            border-top-left-radius:10px/50px;
            border-top-right-radius:10px/50px; */
            transform-origin: 3px 50px;
        }
        .minute {
            width: 4px;
            height: 60px;
            background: #d15f2a;
            position: absolute;
            top: 40px;
            left: 98px;
            border-radius:10px 10px 0 0 /50px;
            transform-origin: 2px 60px;
        }
        .second {
            width: 2px;
            height: 70px;
            background: #e0d20c;
            position: absolute;
            top: 30px;
            left: 99px;
            border-radius:10px 10px 0 0 /50px;
            transform-origin: 1px 70px;
        }
        .circle {
            width: 10px;
            height: 10px;
            background: #000000;
            position: absolute;
            top: 95px;
            left: 95px;
            border-radius: 50%;
        }
    </style>
</head>
<body>
<div id="time">
    <ul>
        <!-- <li style="transform: rotate(0deg);"></li>
        <li style="transform: rotate(6deg);"></li>
        <li style="transform: rotate(12deg);"></li>
        <li style="transform: rotate(18deg);"></li>
        <li style="transform: rotate(24deg);"></li> -->
    </ul>
    <div class="hour"></div>    
    <div class="minute"></div>    
    <div class="second"></div> 
    <div class="circle"></div>   
</div>
<script>
//1.使用JS生成表盘刻度 总共有360度,和60和刻度,所以每个刻度代表6度
var time = document.querySelector("#time");
var ul = time.querySelector("ul");
var hour = time.querySelector(".hour");
var minute = time.querySelector(".minute");
var second = time.querySelector(".second");

var len = 360/6;
var lis = '';
var timer = null;

for (var i = 0; i < len; i++) {
    lis +='<li style="transform: rotate('+ i*6 +'deg);"></li>';
}
ul.innerHTML = lis;

//3.通过定时器,让时分秒中动起来
setTime();
//每次开启定时器之前先关闭
clearInterval(timer);
timer = setInterval(setTime,1000);
function setTime(){
    var date = new Date();
    var seconds = date.getSeconds();
    var minutes = date.getMinutes() + seconds/60;
    var hours = date.getHours() + minutes/60;
    //4.时针和分针不能一次跳一大格,需要有小数点
    second.style.transform = 'rotate('+ seconds*6 +'deg)';
    minute.style.transform = 'rotate('+ minutes*6 +'deg)';
    //一个小时30度
    hour.style.transform = 'rotate('+ hours*30 +'deg)';
}
</script>    
</body>
</html>

3.7 transform应用二(水滴按钮)

需求:鼠标移入时,有动画效果。

问题:使用hover实现时,发现鼠标离开后,动画就会停止了。

div:hover {
            animation: .5s move linear;
        }

解决: 要实现复杂的动画就需要结合JS实现,且通过animationend监听事件结束后,清除已有样式,才会在鼠标再次移入时有动画效果

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>水滴按钮</title>
    <style>
        @keyframes move {
            0% {
                transform: scaleX(1) scaleY(.75);
            }
            10% {
                transform: scaleY(.8) scaleX(.95);
            }
            20% {
                transform: scaleX(.9) scaleY(.85);
            }
            30% {
                transform: scaleY(.9) scaleX(.85);
            }
            40% {
                transform: scaleX(.8) scaleY(.95);
            }
            50% {
                transform: scaleY(1) scaleX(.75);
            }
            60% {
                transform: scaleX(.8) scaleY(.95);
            }
            70% {
                transform: scaleY(.85) scaleX(.9);
            }
            80% {
                transform: scaleX(.85) scaleY(.9);
            }
            90% {
                transform: scaleY(.8) scaleX(.95);
            }
            100% {
                transform: scaleX(1) scaleY(.75);
            }
        }
        div {
            width: 50px;
            height: 50px;
            margin: 100px auto;
            background: rgb(138, 137, 137);
            border-radius: 50%;
            font: 18px/50px "宋体";
            font-weight: bold;
            color: #ffffff;
            text-align: center;
        }
        /* div:hover {
            animation: .5s move linear;
        } */
        .btn_hover {
            animation: .4s move linear 2; 
        }
    </style>
</head>
<body>
    <div class="btn">More</div>
<script>
var btn = document.querySelector(".btn");
var water = document.querySelector(".water");
btn.addEventListener('mouseover',function(){
    btn.classList.add("btn_hover");
});
btn.addEventListener('animationend',function(){
    btn.classList.remove("btn_hover");
});
</script>
</body>
</html>

3.8 JS获取transform (matrix)

transform可供操作的只有一个值matrix,即矩阵。transform 2D 矩阵共有9位,默认三位不能操作,可供操作的只有6位。transform 3D 矩阵共有16位,默认四位不能操作,可供操作的只有12位。

而transform的旋转,斜切,位移,缩放都是CSS封装的操作matrix的各种方法。

matrix是不可逆的,即通过matrix是不能推出元素之前做过旋转,斜切,位移,缩放哪些操作。所以transform的操作不能获取。

- matrix(a,b,c,d,e,f) 矩阵函数 :默认值 matrix(1,0,0,1,0,0);

- 通过矩阵实现缩放

  1. x轴缩放 a=x*a   c=x*c   e=x*e;
  2. y轴缩放 b=y*b   d=y*d   f=y*f;

- 通过矩阵实现位移

  1. x轴位移: e=e+x
  2. y轴位移: f=f+y

- 通过矩阵实现倾斜

  1. x轴倾斜: c=Math.tan(xDeg/180*Math.PI)
  2. y轴倾斜: b=Math.tan(yDeg/180*Math.PI)

- 通过矩阵实现旋转

  1. a=Math.cos(deg/180*Math.PI);
  2. b=Math.sin(deg/180*Math.PI);
  3. c=-Math.sin(deg/180*Math.PI);
  4. d=Math.cos(deg/180*Math.PI);

弧度转角度:deg*180/Math.PI

角度转弧度:deg/180*Math.PI

三角函数

  1. tan正切:直角三角形中,对边与邻边的比值
  2. sin正弦:直角三角形中,对边与斜边的比值
  3. cos余弦:直角三角形中,邻边与斜边的比值

需求:每次点击时,让box在现有基础上,继续旋转15度。

所以一开始需要获取到box的旋转角度,但是transform是获取不到的,只能操作其上面的matrix矩阵。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #box {
            width: 100px;
            height: 100px;
            background: red;
            margin: 100px auto;
            font: 80px/100px "宋体";
            text-align: center;
        }
    </style>
</head>
<body>
    <div id="box">上</div>
    <script>
        (function(){
            // matrix(1,0,0,1,0,0);
            var a = 1;
            var b = 0;
            var c = 0;
            var d = 1;
            var e = 0;
            var f = 0;
            //matrix(a,b,c,d,e,f)
            var box = document.querySelector("#box");
            
            // 位移 x轴位移: e=e+x
            function translateX(x){
                e += x;
                box.style.transform = 'matrix('+a+','+b+','+c+','+d+','+e+','+f+')';//matrix(a,b,c,d,e,f)
            }

            // 位移 y轴位移: f=f+y
            function translateY(y){
                f += y;
                box.style.transform = 'matrix('+a+','+b+','+c+','+d+','+e+','+f+')';
            }
            //缩放 x轴缩放 a=x*a    c=x*c     e=x*e;
            function scaleX(x){
                a=x*a;
                c=x*c;
                e=x*e;
                box.style.transform = 'matrix('+a+','+b+','+c+','+d+','+e+','+f+')';
            }
            //缩放 y轴缩放 b=y*b   d=y*d     f=y*f;
            function scaleY(y){
                b=y*b;
                d=y*d;
                f=y*f;
                box.style.transform = 'matrix('+a+','+b+','+c+','+d+','+e+','+f+')';
            }
            // 通过矩阵实现倾斜 
            // x轴倾斜 c=Math.tan(xDeg/180*Math.PI)
            function skewX(xDeg){
                c=Math.tan(xDeg/180*Math.PI);
                box.style.transform = 'matrix('+a+','+b+','+c+','+d+','+e+','+f+')';
            }
            // y轴倾斜: b=Math.tan(yDeg/180*Math.PI)
            function skewY(yDeg){
                b=Math.tan(yDeg/180*Math.PI);
                box.style.transform = 'matrix('+a+','+b+','+c+','+d+','+e+','+f+')';
            }
            //通过矩阵实现旋转
            function rotate(deg){
                a=Math.cos(deg/180*Math.PI); 
                b=Math.sin(deg/180*Math.PI);
                c=Math.sin(deg/180*Math.PI);
		        d=Math.cos(deg/180*Math.PI);
                box.style.transform = 'matrix('+a+','+b+','+c+','+d+','+e+','+f+')';
            }

            box.onclick = function(){
                //x轴位移
                // translateX(10);

                //y轴位移
                // translateY(20);

                //x轴缩放
                // scaleX(.5);

                //y轴缩放
                // scaleY(.5);

                //x轴倾斜
                // skewX(40);

                //y轴倾斜
                // skewY(60);

                rotate(60);
            };
        })();
    </script>
</body>
</html>

4.transform 3D

4.1 transform 3D方法

- 3D旋转:

  1. rotateX() 围绕X轴旋转(上下翻转)
  2. rotateY() 围绕Y轴旋转(左右翻转)
  3. rotateZ() 围绕Z轴旋转

- 3D位移

  • translateZ()  Z轴位移,近大远小。单位像素

- transform-style :3D空间。父级进行3D变换时,是否保留子元素的3D变换。默认值flat平面不保留子级3D变换;preserve-3d保留。

- perspective :景深,在3D变换中,模拟我们的视角,去看元素在Z轴的距离。一般在父级添加。

- perspective-origin:景深基点,在3D变换中,我们的视角点。一般在父级添加。

- backface-visibility: hidden;隐藏背面:加给 3d 每一个面 。背面:和父级角度相对面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #wrap {
            width: 600px;
            height: 600px;
            border: 1px solid black;
            margin: 0 auto;
            /* 
                景深:在3D变换中,模拟我们的视角去看元素与Z轴的距离
            
             */
            perspective: 200px;
        }
        #box {
            width: 200px;
            height: 200px;
            background: red;
            margin: 200px auto;
            font: 140px/200px "宋体";
            text-align: center;
            color: white;
            transition: 3s;
            /* 3D空间,父级元素变换时,是否保留子元素的3D变换 preserve-3d保留,flat不保留*/
            transform-style: preserve-3d;
        }
        #wrap:hover span {
            display: block;
            width: 200px;
            height: 200px;
            background: yellow;
            transform: rotateX(45deg);
        }
        #wrap:hover #box{
            /* 3D变换,围绕X轴旋转 */
            transform: rotateY(360deg);
            /* 3D变换,围绕Y轴旋转 */
            /* transform: rotateY(180deg); */
            /* 3D变换,围绕Y轴旋转 */
            /* transform: rotateZ(180deg); */
            /* 3D变换,Z轴位移 */
            /* transform: translateZ(60px); */

        }
    </style>
</head>
<body>
    <div id="wrap">
        <div id="box">
            <span></span>
        </div>
    </div>
</body>
</html>

4.2 搭建立方体

思路:先写好立方体的6个面,在旋转每个面。

景深大小;各个面的绝对定位;各个面的旋转基点transform-origin

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #wrap {
                width: 600px;
                height: 600px;
                border: 1px solid black;
                margin: 0 auto;
                perspective: 1000px;
                /* 景深基点 */
                perspective-origin: center center;
        }
        #box {
            width: 200px;
            height: 200px;
            position: relative;
            transform-style: preserve-3d;
            transition: 3s;
        }
        #wrap #box div{
            width: 200px;
            height: 200px;
            font: 140px/200px "宋体";
            text-align: center;
            position: absolute;
            opacity: .5;
        }
        #box div:nth-child(1) {
            background: rgb(255, 0, 0);
            top: 0;
            left: 200px;
            /* 只围绕底部,可只写bottom */
            transform-origin: bottom;
            transform: rotateX(90deg);
        }
        #box div:nth-child(2) {
            background: rgb(200, 255, 0);
            top: 200px;
            left: 0;
            transform-origin: right;
            transform: rotateY(-90deg);
        }
        #box div:nth-child(3) {
            background: rgb(0, 255, 115);
            top: 200px;
            left: 200px;
            /* transform-origin: 200px bottom;
            transform: rotateX(90deg); */
        }
        #box div:nth-child(4) {
            background: rgb(0, 153, 255);
            top: 200px;
            left: 400px;
            transform-origin: left;
            transform: rotateY(90deg);
        }
        #box div:nth-child(5) {
            background: rgb(132, 0, 255);
            top: 400px;
            left: 200px;
            transform-origin: top;
            transform: rotateX(-90deg);
        }
        #box div:nth-child(6) {
            background: rgb(255, 0, 200);
            top: 200px;
            left: 200px;
            /* 第6面是在Z轴方向 */
            transform: translateZ(-200px) rotateY(180deg);
        }
        #wrap:hover #box{ 
            transform-origin: 300px 300px;
            transform: rotateY(180deg);
        }
    </style>
</head>
<body>
    <div id="wrap">
        <div id="box">
            <div>1</div>
            <div>2</div>
            <div>3</div>
            <div>4</div>
            <div>5</div>
            <div>6</div>
        </div>
    </div>
</body>
</html>

5.requestAnimationFarme动画帧

人眼每秒可识别24帧,超过24帧在人眼中,就会形成连续动画的错觉。所以使用JS制作动画,不断修改元素的值,就可以实现动画效果。

能使用CSS实现的效果,尽量使用CSS实现,而不是使用JS实现。CSS本身性能会比JS好。另外,一般做动画,能操作transform改变的,不要使用修改left,top等值实现,因为这些属性在解析完成后,会导致回流。

requestAnimationFarme动画帧不是CSS中的动画帧,而是JS中本身存在的,window下的方法。

requestAnimationFarme动画帧与定时器相关区别:动画流畅度和性能都比定时器好。使用定时器做动画有可能丢帧(画面忽然卡一下)。

  1. 计算机显示器刷新频率一般是60Hz,相当于每秒重绘60次
  2. 屏幕每次渲染时就会调用当前次requestAnimationFarme(注意并不是重复调用),看当前次requestAnimationFarme有没有执行新的东西
  3. 动画帧采用的是系统时间间隔,它与计算机屏幕重绘频率保持一致(高性能,视觉佳)
  4. 定时器执行时间间隔(16.6666循环)不精确
  5. 兼容性 IE9 以下不兼容
  6. 与setTimeOut类似,只会执行一次,如果希望执行动画效果,连续执行,就需要使用递归进行重复调用
  7. 注意递归调用多次都需要存储requestAnimationFrame动画帧编号,便于取消requestAnimationFrame
  • 开启动画帧:index requestAnimationFrame(fn):参数为函数(只有一个参数),返回值为动画帧对应编号
  • 关闭动画帧:cancelAnimationFrame()

需求:使用JS实现跑停动画

使用定时器实现:

使用间隔定时器,不断改变div的transform的值

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #box {
            width: 100px;
            height: 100px;
            background: red;
            margin-bottom: 50px;
        }
    </style>
</head>
<body>
    <div id="box"></div>
    <button>跑</button>
    <button>停</button>
    <script>
        var box = document.querySelector("#box");
        var l = 0;
        var btns = document.querySelectorAll("button");
        var timer = null;
        btns[0].onclick = function(){
            clearInterval(timer);
            timer = setInterval(function(){
                l +=10;
                box.style.transform = 'translateX('+ l +'px)';
            },50);
        };
        btns[1].onclick = function(){
            clearInterval(timer);
        };
    </script>
</body>
</html>

问题:发现使用定时器实现的动画,并不流畅。

解决:使用requestAnimationFrame实现

注意递归调用多次都需要存储requestAnimationFrame动画帧编号,便于取消requestAnimationFrame

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #box {
            width: 100px;
            height: 100px;
            background: red;
            margin-bottom: 50px;
        }
    </style>
</head>
<body>
    <div id="box"></div>
    <button>跑</button>
    <button>停</button>
    <script>
        var box = document.querySelector("#box");
        var l = 0;
        var btns = document.querySelectorAll("button");
        var runner = 0;
        btns[0].onclick = function(){
            cancelAnimationFrame(runner);
            runner = requestAnimationFrame(setTranslateX);
            function setTranslateX(){
                l +=1;
                box.style.transform = 'translateX('+ l +'px)';
                runner = requestAnimationFrame(setTranslateX);
            }
        };

        btns[1].onclick = function(){
            cancelAnimationFrame(runner);
        };
    </script>
</body>
</html>

6.Tween 动画公式

- Tween 参数解析

  • - t: current time(当前时间-当前运动次数),动画执行到第几次(动画已经消耗的时间)
  • - b: beginning value(动画开始前的初始值)
  • - c: change in value(变化量:动画初始值和目标点之间的差值)
  • - d: duration(持续时间-运动总次数:动画执行总次数)

Tween动画算法:

/*
    Tween 动画算法
*/    
var Tween = {
	linear: function (t, b, c, d){  //匀速
		return c*t/d + b;
	},
	easeIn: function(t, b, c, d){  //加速曲线
		return c*(t/=d)*t + b;
	},
	easeOut: function(t, b, c, d){  //减速曲线
		return -c *(t/=d)*(t-2) + b;
	},
	easeBoth: function(t, b, c, d){  //加速减速曲线
		if ((t/=d/2) < 1) {
			return c/2*t*t + b;
		}
		return -c/2 * ((--t)*(t-2) - 1) + b;
	},
	easeInStrong: function(t, b, c, d){  //加加速曲线
		return c*(t/=d)*t*t*t + b;
	},
	easeOutStrong: function(t, b, c, d){  //减减速曲线
		return -c * ((t=t/d-1)*t*t*t - 1) + b;
	},
	easeBothStrong: function(t, b, c, d){  //加加速减减速曲线
		if ((t/=d/2) < 1) {
			return c/2*t*t*t*t + b;
		}
		return -c/2 * ((t-=2)*t*t*t - 2) + b;
	},
	elasticIn: function(t, b, c, d, a, p){  //正弦衰减曲线(弹动渐入)
		if (t === 0) { 
			return b; 
		}
		if ( (t /= d) == 1 ) {
			return b+c; 
		}
		if (!p) {
			p=d*0.3; 
		}
		if (!a || a < Math.abs(c)) {
			a = c; 
			var s = p/4;
		} else {
			var s = p/(2*Math.PI) * Math.asin (c/a);
		}
		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
	},
	elasticOut: function(t, b, c, d, a, p){    //*正弦增强曲线(弹动渐出)
		if (t === 0) {
			return b;
		}
		if ( (t /= d) == 1 ) {
			return b+c;
		}
		if (!p) {
			p=d*0.3;
		}
		if (!a || a < Math.abs(c)) {
			a = c;
			var s = p / 4;
		} else {
			var s = p/(2*Math.PI) * Math.asin (c/a);
		}
		return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
	},    
	elasticBoth: function(t, b, c, d, a, p){ 
		if (t === 0) {
			return b;
		}
		if ( (t /= d/2) == 2 ) {
			return b+c;
		}
		if (!p) {
			p = d*(0.3*1.5);
		}
		if ( !a || a < Math.abs(c) ) {
			a = c; 
			var s = p/4;
		}
		else {
			var s = p/(2*Math.PI) * Math.asin (c/a);
		}
		if (t < 1) {
			return - 0.5*(a*Math.pow(2,10*(t-=1)) * 
					Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
		}
		return a*Math.pow(2,-10*(t-=1)) * 
				Math.sin( (t*d-s)*(2*Math.PI)/p )*0.5 + c + b;
	},
	backIn: function(t, b, c, d, s){     //回退加速(回退渐入)
		if (typeof s == 'undefined') {
		   s = 1.70158;
		}
		return c*(t/=d)*t*((s+1)*t - s) + b;
	},
	backOut: function(t, b, c, d, s){
		if (typeof s == 'undefined') {
			s = 1.70158;  //回缩的距离
		}
		return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
	}, 
	backBoth: function(t, b, c, d, s){
		if (typeof s == 'undefined') {
			s = 1.70158; 
		}
		if ((t /= d/2 ) < 1) {
			return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
		}
		return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
	},
	bounceIn: function(t, b, c, d){    //弹球减振(弹球渐出)
		return c - Tween['bounceOut'](d-t, 0, c, d) + b;
	},       
	bounceOut: function(t, b, c, d){//*
		if ((t/=d) < (1/2.75)) {
			return c*(7.5625*t*t) + b;
		} else if (t < (2/2.75)) {
			return c*(7.5625*(t-=(1.5/2.75))*t + 0.75) + b;
		} else if (t < (2.5/2.75)) {
			return c*(7.5625*(t-=(2.25/2.75))*t + 0.9375) + b;
		}
		return c*(7.5625*(t-=(2.625/2.75))*t + 0.984375) + b;
	},      
	bounceBoth: function(t, b, c, d){
		if (t < d/2) {
			return Tween['bounceIn'](t*2, 0, c, d) * 0.5 + b;
		}
		return Tween['bounceOut'](t*2-d, 0, c, d) * 0.5 + c*0.5 + b;
	}
};

Tween的使用:

    (function(){
        var box = document.querySelector("#box");
        //easeOut: function(t, b, c, d)     //减速曲线
        var t = 0;//动画执行到第几次(动画已经消耗的时间)
        var b = 0;//动画开始前的初始值
        var c = 500;//动画初始值和目标点的差值
        var d = 10;//动画执行总次数
        var s = 100;//回退的距离
        var btns = document.querySelectorAll("button");
        btns[0].onclick = function(){
            t++;
            var val = Tween["easeOut"](t, b, c, d);
            box.style.transform = 'translateX('+ val +'px)';
        };

    })();

动画帧中使用Tween:

    (function(){
        var box = document.querySelector("#box");
        //bounceIn: function(t, b, c, d)     //弹球减振(弹球渐出)
        var t = 0;//动画执行到第几次(动画已经消耗的时间)
        var b = 0;//动画开始前的初始值(如初始宽度等)
        var c = 500;//动画初始值和目标点的差值(如希望动画走600,初始值为100,则差值应设置为500)
        var d = 20;//动画执行总次数
        var s = 100;//回退的距离
        var btns = document.querySelectorAll("button");
        var timer = 0;
        btns[0].onclick = function(){
            cancelAnimationFrame(timer);
            timer = requestAnimationFrame(move);
            function move(){
                t++;
                var val = Tween["bounceIn"](t, b, c, d);
                box.style.transform = 'translateX('+ val +'px)';
                console.log(t);
                
                //判断当执行次数到达设置的次数时,不再继续执行
                if(t<d){
                    timer = requestAnimationFrame(move);
                }
            }
        };
        btns[1].onclick = function(){
            cancelAnimationFrame(timer);
        };
    })();

7.动画框架mTween的使用

    - css(el,attr[,value]) css 函数 设置或获取样式
      - 注意: 非数值类样式的处理
      - 注意: transform 相关样式的处理问题
    - mTween(option) 动画函数
      - option:{
         el: element要动画的元素,
         attr: {
           要动画的样式: '目标值',
           要动画的样式2: '目标值'
         },
         duration: nub||op, 动画时间
          - op: {
            multiple: 根据距离计算动画时间比例
            max: 动画最大时间
            min: 动画最小时间
          }
         fx:'动画形式',
         moveing: function(){}, 动画执行中回调
         cb:function(){} 动画执行之后回调
      }
    - mTween.stop(el) 停止某个元素的动画

css(el,attr[,value]) css 函数 设置或获取样式,两个参数时是获取,三个参数时是设置样式。因为css方法主要是配合动画使用的,所以单位进行了处理。

  • - 注意: 非数值类样式的处理:非数值类样式只能获取,不能设置,更不能用于动画;
  • - 注意: transform 相关样式的处理问题:transform 不能获取计算后样式,所以必须通过css()方法进行操作,且必须先设置(给定初始化值),再获取即可

只有以下样式可以用于动画:

    "background"
    "opacity"
    "rotate",
    "rotateX",
    "rotateY",
    "rotateZ",
    "translateX",
    "translateY",
    "translateZ",
    "scale",
    "scaleX",
    "scaleY",
    "skewX",
    "skewY"
    "width",
    "height",
    "left",
    "top",
    "right",
    "bottom",
    "marginBottom",
    "marginleft",
    "marginRight",
    "marginTop",
    "paddingLeft",
    "paddingRight",
    "paddingTop",
    "paddingBottom"

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #box {
            position: absolute;
            left: 0;
            top: 100px;
            width: 100px;
            height: 100px;
            background: red;
        }
    </style>
</head>
<body>
<div id="box"></div>
<script src="mTween.js"></script>
<script>
(function(){
    var box = document.querySelector("#box");
    //获取样式
    console.log(css(box,"width"));//100注意没有单位,mTween框架已经对单位进行了处理
    //设置样式
    css(box,"height",400);
    //非数值类样式只能获取,不能设置,但是颜色可以设置
    css(box,"background","blue");
    console.log(css(box,"background"));//rgb(0, 0, 255) none repeat scroll 0% 0% / auto padding-box border-box
    css(box,"float","left");
    console.log(css(box,"float"));//none
    
})();    
</script>    
</body>
</html>

transform 相关样式的处理问题:

    //transform不能获取计算后样式,所以所有操作必须通过css()方法进行。且需要先设置再获取
    console.log(css(box,"translateX"));//undefined
    css(box,"translateX",20);
    console.log(css(box,"translateX"));//20

mTween同时设置多个值:需要传一个对象进去

    //同时设置多个样式,需要传入一个对象
    css(box,{
        "width":200,
        "height":200,
        "background":"blue",
        "translateX":200
    });

动画框架的使用:

设置属性值:

            mTween({
                el:box,
                attr:{
                    width:200,
                    height:200,
                    // translateX:20
                    rotate:360
                });

设置动画持续时间: 

 //动画持续时间(单位秒),默认时间400毫秒
                // duration:1000
                //动画持续时间第二种用法,比例设置:
                duration:{
                    multiple: 1,//根据距离计算动画时间比例:获取到attr样式中最大差值,计算时间,此处360,然后360*1 就为360毫秒
                    min: 200,//动画最大时间:如果 multiple*最大差值的时间小于min,则使用min的时间,非min和max,则使用multiple*最大差值的时间
                    max: 1000//动画最小时间:如果 multiple*最大差值的时间大于max,则使用max的时间,非min和max,则使用multiple*最大差值的时间
                },

设置动画执行样式:

//动画执行样式(参照transition)
                fx:"linear",

设置回调函数:动画执行中和动画执行结束 回调

                //回调:动画执行中和动画执行结束
                cb:function(){
                    console.log("动画执行完成");
                    //控制动画过程:想在动画执行完成后,再执行其他的动画,直接在cb函数值再进行调用
                    mTween({
                        el:box,
                        attr:{
                            translateX:40  
                        }
                    });
                    
                },
                //动画执行过程中:计算机每秒执行60次,在duration给定时间内,会打印duration/60次。即一次打印用时1000/60 = 16.6666666次。总共有16.666666*3次约等于50次
                moveing:function(){
                    console.log("动画执行过程中");//打印66次
                }

动画执行过程中结束动画:

                //想在动画执行过程中结束动画,mTween.stop(动画执行的元素)
                mTween.stop(box);

完整示例:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #box {
            width: 100px;
            height: 100px;
            background: red;
            margin-bottom: 50px;
        }
    </style>
</head>
<body>
    <div id="box"></div>
    <button>执行</button>
    <button>停</button>
    <script src="mTween.js"></script>
    <script>
        var box = document.querySelector("#box");
        var btns = document.querySelectorAll("button");
        btns[0].onclick = function(){
            //如果要设置transform,一定要先通过css()方法进行设置
            css(box,"translateX",0);
            css(box,"rotate",0);
            //动画框架的使用
            mTween({
                el:box,
                attr:{
                    width:200,
                    height:200,
                    // translateX:20
                    rotate:360
                },

                //动画持续时间(单位秒),默认时间400毫秒
                // duration:1000
                //动画持续时间第二种用法,比例设置:
                duration:{
                    multiple: 1,//根据距离计算动画时间比例:获取到attr样式中最大差值,计算时间,此处360,然后360*1 就为360毫秒
                    min: 200,//动画最大时间:如果 multiple*最大差值的时间小于min,则使用min的时间,非min和max,则使用multiple*最大差值的时间
                    max: 1000//动画最小时间:如果 multiple*最大差值的时间大于max,则使用max的时间,非min和max,则使用multiple*最大差值的时间
                },

                //动画执行样式(参照transition)
                fx:"linear",

                //回调:动画执行中和动画执行结束
                cb:function(){
                    console.log("动画执行完成");
                    //控制动画过程:想在动画执行完成后,再执行其他的动画,直接在cb函数值再进行调用
                    mTween({
                        el:box,
                        attr:{
                            translateX:40  
                        }
                    });
                    
                },
                //动画执行过程中:计算机每秒执行60次,在duration给定时间内,会打印duration/60次。即一次打印用时1000/60 = 16.6666666次。总共有16.666666*3次约等于50次
                moveing:function(){
                    console.log("动画执行过程中");//打印66次
                }
            });
            
            btns[1].onclick = function(){
                //想在动画执行过程中结束动画,mTween.stop(动画执行的元素)
                mTween.stop(box);
            };
        };

    </script>
</body>
</html>

8.动画框架mTween示例—无缝滚动幻灯片

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值