原稿:语雀 · CSS 动画 & 常见绘制 · 原目录:CSS › CSS 动画 & 常见绘制

CSS transform

CSS transform允许旋转,缩放,倾斜或平移给定block元素。transform的行为是通过修改CSS视觉格式化模型的坐标空间来实现的——各种变化由transform func相乘叠加,一个func对应一重图层复合操作,因此如果动画中使用transform进行元素坐标和大小调整时无需重排和重绘,对应图层已经被计算出


CSS animation

参考链接:CSS动画-阮一峰

CSS动画主要有三个重要部分:transition(过渡)、animation(动画)、keyframe(关键帧)

transition

曾经CSS中所有的样式都是在即时施加的,CSS3中引入了“transition-过渡”概念,允许指定状态改变所需的时间,借此可以实现延时效果,transition相关有4个属性:

img{

   height:15px;

   width:15px;

} img:hover{

   height: 450px;

   width: 450px;

}

img{

   transition-property: height;

   transition-duration: 1s;

   transition-delay: 1s;

   transition-timing-function: ease;

   /*transition:height 1s 1s ease*/

}

  • property:指定施加过渡的属性
  • duration:指定状态改变持续时间
  • delay:触发到真正状态改变的推迟
  • time-func:速度函数,默认非匀速
  • 四种属性可简写为transition

上面代码实现:hover图片后width立刻50px->450px,1s后高度在1s内由50px->450px

transition局限

transition的优点在于简单易用,但是它有几个很大的局限 2. transition需要事件触发,所以没法在网页加载时自动发生 4. transition是一次性的,不能重复发生,除非一再触发 6. transition只能定义开始状态和结束状态,不能定义中间状态,也就是说只有两个状态 8. 一条transition规则,只能定义一个属性的变化,不能涉及多个属性

animation

CSS animation为了解决transition的局限

 div:hover {

 animation-name: rainbow;

 animation-duration: 1s;

 animation-timing-function: linear;

 animation-delay: 1s;

 animation-fill-mode:forwards;

 animation-direction: normal;

 animation-iteration-count: 3;

/* animation: 1s 1s rainbow linear 3 forwards normal;*/

}

大部分animation属性类似transition,其中:

  • name:指定应用的一系列动画,每个名称代表一个由keyframes定义的动画序列
  • fill-mode:设置动画在执行之前和之后如何将样式应用于其目标
  • none为回到动画未开始状态
  • backwards/forwards是回到并保持第一或最后一帧动画
  • direction:这是动画运行方向
  • 向前ABAB、向后BABA、正交替ABBA、反交替BAAB
  • teration-count:指定动画迭代次数0~infinite

keyframes

keyframes关键字用来定义动画的各个状态,百分比对应了duration的执行情况,0%可以用from代表,100%可以用to代表

@keyframes rainbow {

 from { background: #c00 }

 50% { background: orange }

 to { background: yellowgreen }

}

通过animation-play-state属性可以设置动画执行时的状态,默认是回到初始状态,右侧代码实现鼠标hover时执行动画,松开时保持当前帧,而不是回到初始

div {

   animation: spin 1s linear infinite;

   animation-play-state: paused;

}

div:hover {

 animation-play-state: running;

}


实现1:绘制圆&三角形

参考链接:纯 CSS 实现绘制各种三角形(各种角度)

圆形比较容易,border-radius:50%,圆角半径设置为盒子的50%即可

三角形,关键是认识到CSS中的每条边框实际是四个三角拼起来的,在其中扣去content即成为border,如果content的宽高都是0,可以得到一个正方形👉

CSS 动画 & 常见绘制 配图
CSS 动画 & 常见绘制 配图

根据这个原理,使用“空content+透明border+非透明border”即可实现三角形

顶点朝上的 等腰三角形

      .triangle-up {

       width: 0;

       height: 0;

       border-left: 50px solid transparent;

       border-right: 50px solid transparent;

       border-bottom: 100px solid red;

     }

image.png

顶点在右上的 等腰直角三角形

      .triangle-top-right {

       width: 0;

       height: 0;

       border-left: 50px solid transparent;

       border-bottom: 50px solid transparent;

       border-right: 50px solid red;

       border-top: 50px solid red;

     }

image.png

实现2:渐变色

渐变实际上是通过background-image的梯度<gradient>实现,许多在线设计器可生成渐变代码

.a {
    width: 200px;
  height: 100px;
  margin: 10px 0 10px 0;
  background-image: linear-gradient(105deg, rgba(0,249,255,1) 39%, rgba(51,56,57,1) 96%);
}

.b {
  width: 200px;
  height: 100px;
  background-image: radial-gradient(circle, rgba(0,249,255,1) 39%, rgba(51,56,57,1) 96%);
  background-size: 100px 50px;
}

<div class="wrapper">


</div>

实现效果如下:

  • 左图使用线性梯度,规定渐变轴为105°(默认0°即水平),给定两个渐变色,百分比代表了渐变色终点
  • 右图使用径向梯度,给定两个渐变色
image.png

实现3:秒针转动效果

使用 animation

<!DOCTYPE html><html>  <head>    <meta charset="utf-8" />    <title>clock</title>    <style>      /* 表盘 */      .second-bar-box {        width: 100px;        height: 100px;        /* 画圆:圆角半径设为盒子宽度50% */        border-radius: 50%;        border: 1px solid red;        /* 表盘作为定位上下文 */        position: relative;      }      /* 秒针 */      .second-bar {        position: absolute;        left: 50%;        top: 0;        width: 2px;        height: 50px;        margin-left: -1px;        background-color: #ccc;        animation: barMove 60s linear infinite;        transform-origin: center bottom;      }      @keyframes barMove {        0% {          transform: rotateZ(0deg);        }        100% {          transform: rotateZ(360deg);        }
  1. 动画代码:animation: barMove 60s linear infinite;

指定barMove作为关键帧,持续时间60s,匀速、循环执行 2. 设置变换基点:transform-origin: center bottom

使用transform-origin设定了元素变换的原点为x轴中点,y轴底部(联系秒针,很好理解) 2. 关键帧代码:0% { transform: rotateZ(8deg); } 100% { transform: rotateZ(360deg); }

使用变换函数rotateZ,以z轴(想成垂直插进屏幕的轴)旋转,需提前设置变换基点,从0°到360°

image.png

使用 rAF()

rAF() 可以用来执行无限循环的动画,但是其回调的间隔固定为每秒60次,为了实现精确计时,需要稍微设置: 2. rAF() 60s 内执行3600次 4. 秒针60s内转动360deg,360/3600=0.1deg

则每次回调中使得 retateZ 的角度增长0.1deg,增长到360deg后归零

注意 <script> 需要放在 <body> 后,否则无法获取到文档中的 bar

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>clock</title>
    <style>
      /* 表盘 */
      .second-bar-box {
        width: 100px;
        height: 100px;
        /* 画圆:圆角半径设为盒子宽度50% */
        border-radius: 50%;
        border: 1px solid red;
        /* 表盘作为定位上下文 */
        position: relative;
      }
      /* 秒针 */
      .second-bar {
        position: absolute;
        left: 50%;
        top: 0;
        width: 2px;
        height: 50px;
        margin-left: -1px;
        background-color: #ccc;
        transform-origin: center bottom;
      }
    </style>
  </head>
  <body>
    <div class="second-bar-box">

    </div>
  </body>
  <script>
    let flag = true;

经过测试,60s 时秒针距离12点位置差距极小,因此使用 rAF() 是一个可取的方法


实现4:自动轮播图

参考链接:css实现轮播图

如果要实现一个n张图片(每张宽w,高h)的自动轮播

基本布局

  • 最外层容器slide-container,规定宽高等于w、h,设置overflow:hidden,隐藏溢出内容
  • 父盒子slide-items,规定该盒子宽度至少为n*w,该盒子用于放置所有的轮播图
  • 子盒子slide-item,规定宽高等于w、h,每个图片是一个item,设置float:left,图片会依次按float布局放置到slide-items,构成一行,宽度n*w

动画

有了以上的布局,先不考虑动画,单纯实现当前图片的改变,可以给slide-items设置margin-left:

  • margin-left:0 时,显示第一个item,右侧(n-1)*w宽内容溢出
  • margin-left:-w 时,显示第二个item,左侧w宽、右侧(n-2)*w宽的items内容溢出
  •  ... ...
  • margin-left:-(n-1)*w时,显示最后一个item,左侧(n-1)*w宽内容溢出

加上动画只需在slide-items中设置keyframes,一段时间改变margin-left即可

下面实现一个如下的轮播图

slide.gif
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>slide</title>
    <style>
      .slide-container {
        overflow: hidden;
        width: 300px;
        height: 100px;
        margin: auto;
        text-align: center;
        color: #fff;
      }

      .slide-items {
        /* width要大于等于所有slide-item之和 */
        width: 900px;
        animation: marginLeft 10.5s infinite;
      }

      .slide-items .slide-item {
        float: left;
        width: 300px;
        height: 100px;
        line-height: 100px;
        font-size: 36px;
      }

      .slide-items .slide-item:nth-child(1) {
        background: #9fa8ef;
      }

      .slide-items .slide-item:nth-child(2) {
        background: #ef9fb1;
      }

      .slide-items .slide-item:nth-child(3) {
        background: #9fefc3;
      }

      @keyframes marginLeft {
        /* 0%-25% duration保持margin-left: 0 */
        0%,25% {
          margin-left: 0;
        }
        /* 10% duration进行状态改变,变成margin-left: -300px*/
        /* 35%-65% duration保持margin-left: -300px */
        35%,65% {
          margin-left: -300px;
        }
        /* 下面类似 */
        75%,95% {
          margin-left: -600px;
        }

        100% {
          margin-left: 0;
        }
      }
    </style>
  </head>
  <body>
    <div class="slide-container">
      <div class="slide-items">
        <div class="slide-item">one</div>