Animation

598 阅读6分钟

CSS animation 属性是 animation-name,animation-duration, animation-timing-function,animation-delay,animation-iteration-count,animation-direction,animation-fill-mode 和 animation-play-state 属性的一个简写属性形式。

描述
animation-name规定需要绑定到选择器的 keyframe 名称。
animation-duration规定完成动画所花费的时间,以秒或毫秒计。
animation-timing-function规定动画的速度曲线。
animation-delay规定在动画开始之前的延迟。
animation-iteration-count规定动画应该播放的次数。
animation-direction规定是否应该轮流反向播放动画。
animation-fill-mode设置CSS动画在执行之前和之后如何将样式应用于其目标。
animation-play-state定义一个动画是否运行或者暂停。

接下来我们来细细学习一下这些属性的妙用。

@keyframes

与 transition 相比,@keyframes 通过定义关键帧来控制动画的效果。

  • from/0% 表示动画的第一帧,也就是动画的起始状态。这里要与元素本身的状态区别。
  • to/100% 表示动画的最后一帧,动画结束时的状态,也要与元素本身的状态区别。
  • X%(0<= X <= 100),表示动画 X% 的动画状态。

animation-name 和 animation-duration

animation-name 和 animation-duration 分别表示我们要使用的动画 keyframe 的名称,以及动画运行的时间。

<style>
    .circle{
        width: 40px;
        height: 40px;
        border-radius: 50%;
        background-color: red;
        animation-name: move;
        animation-duration: 1s;
    }
    @keyframes move {
        to{
            transform: translateX(400px);
        }
    }
</style>
<div class="circle"></div>

我们定义了小圆球的动画名称是 move,同时设置了动画运行时间是 1s。

animation-timing-function

animation-timing-function 定义了动画运行速度曲线,默认值是 ease.

描述
linear动画从头到尾的速度是相同
ease默认。动画以低速开始,然后加快,在结束前变慢。
ease-in动画以低速开始。
ease-out动画以低速结束。
ease-in-out动画以低速开始和结束。
cubic-bezier(n,n,n,n)在 cubic-bezier 函数中自己的值。可能的值是从 0 到 1 的数值。

我们定义了 6 个小圆球,从上到下分别用linear、ease、ease-in、 ease-out、ease-in-out、cubic-bezier(.17,.67,1,-0.11)表示其运行速度。我们可以在cubic-bezier.com/#.17,.67,.8… 这里自定义自己的贝塞尔曲线。

animation-delay

可以用来控制动画延迟多久执行。如果是正数 1s,表示延迟 1s 才开始动画;如果是负数 -1s,则不会延迟,相当于提前执行动画,动画的位置从动画执行1s的位置开始算。

延迟执行

<style>
    .circle{
        width: 40px;
        height: 40px;
        border-radius: 50%;
        background-color: red;
        animation-name: move;
        animation-duration: 2s;
        animation-timing-function: linear;
    }
    @keyframes move {
        to{
            transform: translateX(400px);
        }
    }
    .div2{
        animation-delay: 1s;
    }
</style>
<div class="circle"></div>
<div class="circle div2"></div>

提前执行

<style>
    .circle{
        width: 40px;
        height: 40px;
        border-radius: 50%;
        background-color: red;
        animation-name: move;
        animation-duration: 2s;
        animation-timing-function: linear;
    }
    @keyframes move {
        to{
            transform: translateX(400px);
        }
    }
    .div2{
        animation-delay: -1s;
    }
</style>
<div class="circle"></div>
<div class="circle div2"></div>

animation-iteration-count

定义了动画播放的次数,默认值为1,表示动画只播放一次。

  • 可以取 infinite 表示无数次;
  • 可以取正数;正整数和正小数。小数定义循环,来播放动画周期的一部分:例如,0.5 将播放到动画周期的一半。不可为负值。

animation-direction

animation-direction用来控制动画播放的方向。

  • 默认 normal 从 @keyframes 定义的 0%(from) 到 100%(to) 播放的。
  • alternate 表示第一次 0%(from) 到 100%(to) ;第二次从 100%(to) 到 0%(from) 播放;第三次 0%(from) 到 100%(to) ... 依次循环。
  • reverse 与 normal 方向相反;
  • alternate-reverse 与 reverse 方向相反。
描述
normal每个循环内动画向前循环,换言之,每个动画循环结束,动画重置到起点重新开始,这是默认属性。
alternate动画交替反向运行,反向运行时,动画按步后退,同时,带时间功能的函数也反向,比如,ease-in 在反向时成为ease-out。计数取决于开始时是奇数迭代还是偶数迭代
reverse反向运行动画,每周期结束动画由尾到头运行。
alternate-reverse反向交替, 反向开始交替

我们定义了4个小球演示 :

<style>
    .circle{
        width: 40px;
        height: 40px;
        border-radius: 50%;
        background-color: red;
        animation-name: move;
        animation-duration: 2s;
        animation-timing-function: linear;
    }
    @keyframes move {
        to{
            transform: translateX(400px);
        }
    }
    .div2{
        animation-direction: alternate;
        animation-iteration-count: infinite;
    }
    .div3{
        animation-direction: reverse;
    }
    .div4{
        animation-direction: alternate-reverse;
        animation-iteration-count: infinite;
    }
    div+div{
        margin-top: 10px;
    }
</style>
<div class="circle div1" style="margin-top: 100px;"></div>
<div class="circle div2"></div>
<div class="circle div3"></div>
<div class="circle div4"></div> 

第一个小球和第二个动画的播放方向是从 from 到 to; 但是由于第二个小球的动画次数是无数次,所以会交替播放:从左到右、从右到左,从左到右...依次循环。
第三个小球和第四个小球的播放方向是从 to 到 from;由于第四个小球的动画次数是无数次,同第二个类似,会交替播放动画。

animation-fill-mode

可以用来设置动画播放开始播放前的状态以及动画播放结束的状态。

描述
none动画效果 @keyframes 中第一帧和最后一帧不会保留在元素上面。这个也是默认取值
forwards动画效果 @keyframes 中最后一帧会保留在元素上面
backwards动画将在应用于目标时立即应用第一个关键帧中定义的值
both动画将遵循forwards和backwards的规则,从而在两个方向上扩展动画属性。

利用 forwards 实现进度条

<style>
    div{
        margin-top: 200px;
        margin-left: 40px;
    }
    .process{
        width: 400px;
        height: 10px;
        border: 1px solid #ccc;
        box-sizing: content-box;
        position: relative;
    }
    .process::after{
        content: "";
        height: 10px;
        display: inline-block;
        background-color: red;
        position: absolute;
        top: 1px;
        left: 0px;
        animation-name: loading;
        animation-duration: 3s;
        animation-timing-function: linear;
        animation-fill-mode: forwards;
    }
    @keyframes loading{
        0%{
            width: 0;
        }
        50%{
            width: 200px;
        }
        100%{
            width: 400px;
        }
    }
</style>
<div class="process"></div>

animation-fill-mode: forwards; 来表示动画结束的状态会被保留在元素上面。

backwards

在《一个不安分的箭头引发的思考(JS动画实现方式对比)》 juejin.cn/post/690703… 文章中 5个小箭头本身是 opacity:0;隐藏的,最后动画执行完毕后利用了 animation-fill-mode: forwards; 最后让5个小箭头处于显示状态。

我们也可以反过来,5个小箭头刚开始处于显示状态,设置一个动画初始值为隐藏,再利用 animation-fill-mode: backwards; 在动画执行前让箭头保持第一帧的状态。也可以实现哦~大家可以试一下,这里不再赘述。

steps 逐帧动画

animation-timing-function: steps(number, start/end);

  • number是数字,表示将 动画关键帧之间 (0% 到 25%之间)设置为 number 步执行;
  • 第二个参数表示动画第一帧(0%)是如何执行的。start表示直接跳过第一帧(跳过0%),保持第一帧的结束状态;end 表示保持第一帧的开始状态。
  • steps(n,start) 可以简单理解为从第二个开始,steps(n,end) 从第一个开始。
  • www.cnblogs.com/zx0423/p/13…
    <style>
        .wrap{
            width: 200px;
            height: 50px;
            display: flex;
            margin-top: 20px;
            position: relative;
        }
        .item{
            width: 50px;
            height: 50px;
            box-sizing: content-box;
            border-top: 1px solid black;
            border-bottom: 1px solid black;
            border-right: 1px solid black;
            background-color: #f1c40f;
            line-height: 50px;
            text-align: center;
            color: white;
        }
        .item:first-child{
            border-left: 1px solid black;
        }
        .move1{
            width: 50px;
            height: 50px;
            background-color: red;
            animation-name: move;
            animation-duration: 4s;
            animation-timing-function: steps(1, start);
            animation-iteration-count: infinite;
            position: absolute;
            left: 0;
            top: 1px;
        }
        .move2{
            width: 50px;
            height: 50px;
            background-color: red;
            animation-name: move;
            animation-duration: 4s;
            animation-timing-function: steps(1, end);
            animation-iteration-count: infinite;
            position: absolute;
            left: 0;
            top: 1px;
        }
        @keyframes move{
            0%{
                transform: translate(0px);
            }
            25%{
                transform: translate(50px);
            }
            50%{
                transform: translate(100px);
            }
            75%{
                transform: translate(150px);
            }
            to{
                transform: translate(200px);
            }
        }
    </style>
</head>
<body>
    <div class="wrap">
        <div class="item">1</div>
        <div class="item">2</div>
        <div class="item">3</div>
        <div class="item">4</div>
        <div class="move1"></div>
    </div>
    <div class="wrap">
        <div class="item">1</div>
        <div class="item">2</div>
        <div class="item">3</div>
        <div class="item">4</div>
        <div class="move2"></div>
    </div>
</body>


可以看到 animation-timing-function: steps(1, start); 的执行效果是 0% 到 25%、25% 到 50%、50% 到 75% 、75% 到 100% 直接需要 1 步动画完成,并且第一帧 0% 从结束状态开始;animation-timing-function: steps(1, end); 第一帧 0% 从开始状态开始;

补充一个知识点:step-start 效果等于 steps(1,start) ,step-end 效果等同于 steps(1,end)。

轮播图

    <style>
        .swipe{
            height: 100px;
            width: 150px;
            overflow: hidden;
        }
        .wrap{
            height: 100px;
            width: 150px;
            white-space: nowrap;
            font-size: 0;
            animation-name: move;
            animation-duration: 4s;
            animation-timing-function: steps(1, end);
            animation-iteration-count: infinite;
        }
        /* 鼠标悬浮暂停 */
        .wrap:hover{
            animation-play-state: paused;
        }
        .item{
            width: 150px;
            height: 100px;
            color: white;
            line-height: 100px;
            text-align: center;
            font-size: 18px;
            display: inline-block;
        }
        .red{
            background-color: #34495e;
        }
        .green{
            background-color: #27ae60;
        }
        .yellow{
            background-color: #9b59b6;
        }
        .blue{
            background-color: #e74c3c;
        }
        @keyframes move{
            0%{
                transform: translate(0);
            }
            25%{
                transform: translate(-150px);
            }
            50%{
                transform: translate(-300px);
            }
            75%{
                transform: translate(-450px);
            }
            100%{
                transform: translate(-600px);
            }
        }
    </style>
    <div class="swipe">
        <div class="wrap">
            <div class="item red">1</div>
            <div class="item green">2</div>
            <div class="item yellow">3</div>
            <div class="item blue">4</div>
        </div>
    </div>

发光的点


主要利用 box-shadow

    <style>
        .circle{
            width: 14px;
            height: 14px;
            border-radius: 50%;
            background-color: yellow;
            animation: shrink 1s alternate infinite;

        }
        @keyframes shrink{
            to{
                /* x偏移量 | y偏移量 | 阴影模糊半径 | 阴影扩散半径 | 阴影颜色 */ 
                box-shadow: 0 0 14px 3px yellow;
            }
        }
    </style>
    <div class="wrap">
        <div class="circle"></div>
    </div>

圆圈loading

    <style>
        .circle{
            stroke-dasharray: 157;
            animation: loading 2s linear;
        }
        @keyframes loading{
            0%{
                stroke-dashoffset: 157;
            }
            50%{
                stroke-dashoffset: 80;
            }
            100%{
                stroke-dashoffset: 0;
            }
        }
    </style>
    <div class="wrap">
        <svg viewBox="0 0 100 100" width="100" height="100">
            <circle cx="50" cy="50" r="25" stroke="#F1C40F" 
             class="circle" 
             stroke-width="5" stroke-linecap="round" fill="none"/>
        </svg>
    </div>

补充一些 svg 知识:

  • viewBox 用来控制画板的位置和大小;用法要设置4个值:viewbox = "x, y, width, height";
  • stroke-dasharray 设置的是圆弧的样式,这里取了圆的周长:2 * π * r ≈ 157;然后利用 stroke-dashoffset 偏移一点点的出现圆弧。

    <style>
        .circle1{
            transform-origin: center center;
            animation: strokeCircle1 1s linear infinite forwards, rotateCircle 2s linear infinite forwards;
        }
        .circle2{
            transform-origin: center center;
            animation: strokeCircle2 1s linear infinite forwards, rotateCircle 2s linear infinite forwards;
        }

        @keyframes strokeCircle1 {
            0%{
                stroke-dasharray: 10 150;
            }
            50%{
                stroke-dasharray: 30 130;
            }
            100%{
                stroke-dasharray: 50 110;
            }
        }

        @keyframes strokeCircle2 {
            0%{
                stroke-dasharray: 10 150;
            }
            50%{
                stroke-dasharray: 30 130;
            }
            100%{
                stroke-dasharray: 50 110;
            }
        }

        @keyframes rotateCircle{
            from{
                transform: rotate(0deg);
            }
            to{
                transform: rotate(360deg);
            }
        }
    </style>
    <div class="wrap">
        <svg viewBox="0 0 100 100">
            <circle cx="50" cy="50" r="25" stroke="#F1C40F" 
             class="circle1" stroke-dasharray="10 150"
             stroke-width="4" stroke-linecap="round" fill="none"/>
            <circle cx="50" cy="50" r="25" stroke="#7F8C8D" 
            stroke-dashoffset="78" stroke-dasharray="10 150"
            class="circle2"stroke-width="4" stroke-linecap="round" fill="none"/>
        </svg> 
    </div>

www.jianshu.com/p/4422c05ff…
www.cnblogs.com/daisygogogo…

音符loading

    <style>
        .wrap, .bottom-wrap{
            display: flex;
        }
        .item{
            margin-left: 5px;
            position: relative;
            width: 15px;
            height: 0;
        }
        .item::after{
            content: "";
            display: inline-block;
            position: absolute;
            bottom: 0;
            width: 15px;
            height: 50px;
            background-color: crimson;
            border-top-left-radius: 8px;
            border-top-right-radius: 8px;
            
        }
        .item::before{
            content: "";
            display: inline-block;
            position: absolute;
            top: 0;
            width: 15px;
            height: 50px;
            background-color: crimson;
            border-bottom-left-radius: 8px;
            border-bottom-right-radius: 8px;
        }
        .wrap .item:nth-child(1)::after{
            animation: loading 2s infinite;
        }
        .wrap .item:nth-child(2)::after{
            animation: loading 2s infinite 0.2s;
        }
        .wrap .item:nth-child(3)::after{
            animation: loading 2s infinite 0.4s;
        }
        .wrap .item:nth-child(4)::after{
            animation: loading 2s infinite 0.6s;
        }
        .wrap .item:nth-child(5)::after{
            animation: loading 2s infinite 0.8s;
        }

        .wrap .item:nth-child(1)::before{
            animation: loading 2s infinite;
        }
        .wrap .item:nth-child(2)::before{
            animation: loading 2s infinite 0.2s;
        }
        .wrap .item:nth-child(3)::before{
            animation: loading 2s infinite 0.4s;
        }
        .wrap .item:nth-child(4)::before{
            animation: loading 2s infinite 0.6s;
        }
        .wrap .item:nth-child(5)::before{
            animation: loading 2s infinite 0.8s;
        }
        @keyframes loading {
            0%{
                height: 50px;
            }
            50%{
                height: 5px;
            }
            100%{
                height: 50px;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="wrap">
            <div class="item"></div>
            <div class="item"></div>
            <div class="item"></div>
            <div class="item"></div>
            <div class="item"></div>
        </div>
    </div>
</body>

感谢

如果本文有帮助到你的地方,记得点赞哦,这将是我坚持不断创作的动力~