分享一个有趣的animation动画

2,387 阅读3分钟

内容简介

闲着也是闲着,给大家分享一个有趣的css动画的实现过程吧。话不多说,直接上图。

可以看到整个loading过程有一个loading的旋转动画还有一个加载成功之后的动画,loading旋转动画比较好做,只需要让loading图片旋转就行了,我们重点来介绍一下加载成功之后的动画。

loading动画介绍

首先是loading动画的实现,稍微反应快的朋友应该一下子就可以看出来,其实这个loading是个图片来的,那么我们只需要将图片旋转360度即可达到loading旋转的效果。

这个应该会比较好理解,我们直接上代码:
  @function px2vw($size) {
    @return #{$size/360*100}vw;
  };

  .loading {
    width: px2vw(64);
    height: px2vw(64);
    background-image: url('../images/loading.png');
    margin: 0 auto;
    background-size: 100% 100%;
    transform-origin: center;
    animation: loading 2s linear infinite;
  }

  @keyframes loading{
    0%{
      transform: rotate(0);
    }
    100% {
      transform: rotate(360deg);
    }
  }

有必要给大家解释一下的是,作者在开发的过程由于考虑到要做不同手机屏幕的适配,所以选择了用vw进行适配,这种适配效果其实还是不错的,可以解决大部分移动设备的适配问题。

还有一个可能让读者不太理解的是为什么尺寸大小用px2vw()来表示。

这里作者在开发的过程用了sass预处理器,在代码的开头定义了一个自适应的方法:

  @function px2vw($size) {
    @return #{$size/360*100}vw;
  };

其中参数$size传入的是ui设计稿量出来的尺寸,360是ui设计稿的宽度,通过求出对应的比例以达到适配不同移动设备的效果。

加载成功之后的动画

我相信看了这篇文章会让读者好奇的话应该就是这个加载成功后“划圈打勾”动画的实现过程。

实现整个“划圈打勾”的过程只用了上面这样的一张图片,我们也不卖关子了给大家介绍一下实现的过程。

解决这个动画需要考虑到以下的几个点:

  • 如何用动画绘制一个圆圈
  • 划圆圈的过程“√”去哪了
  • 打勾的动画是如何实现的

如何用动画绘制一个圆圈

其实css动画没有大家想的那么高大上,一些动画的实现往往是通过“障眼法”来做到的。 首先我们需要准备两个宽为图片宽度的一半,高与图片的高度相等的两个挡板(其实就是两个div,后续大家就知道为什么叫挡板了),并且两个div要做一个绝对定位,一个定位在左边,一个定位在右边。 先上代码给大家看一下效果。

  <div class="dialog-success">
    <img src="./icon_ok.png" class="icon-ok">
    <div class="left-baffle"></div>
    <div class="right-baffle"></div>
  </div>
  $design-width: 360; // 设计稿宽度360
  @function px2vw($size) {
    @return #{$size/$design-width * 100}vw;
  };
  .dialog-success {
    width: px2vw(64);
    height: px2vw(64);
    margin: 0 auto;
    position: relative;
    .left-baffle {
      width: px2vw(32);
      height: px2vw(64);
      position: absolute;
      background-color: yellow;
      left: 0;
      top: 0;
      opacity: 0.5;
    }
    .right-baffle {
      width: px2vw(32);
      height: px2vw(64);
      position: absolute;
      background-color: pink;
      right: 0;
      top: 0;
      opacity: 0.5;
    }
    .icon-ok {
      width: 100%;
      height: auto;
    }
  }

效果如图:

可能有的小伙伴比较好奇这两个挡板的用途,这里要给读者解释的就是css是如何使用“障眼法”来实现动画的。

不妨试想一下,动画里面的圈是从无到有逆势针旋转出来的,但是图片又是不可能用css画出来的。那如果我们利用一个挡板遮住图片,通过旋转挡板来将图片一点一点的显示出来,那画圈的效果不就出来了吗。为了达到效果,我们首先将两个挡板的背景颜色设置为白色,透明度设置为完全不透明,然后我们来看看挡板沿着圆圈中心点逆时针旋转180度的效果。

(相信通过黄色挡板读者应该能理解挡板为什么挡板要沿着图片圆心旋转吧!!!)

把挡板都设置为白色之后的效果:

我们来看这个旋转过程遗留的一个问题,我们可以从黄色挡板看出当挡板旋转到180度之后,它最后会停留在右边挡板处结束动画,但我们下一个即将要旋转的是右边的挡板,如果左边的挡板单纯停留在右边的话那等到右边的挡板旋转结束后岂不是整个图片又给遮住了?

因此,当左边的挡板旋转到右边之后,我们要让挡板“消失”,这里我们可以利用opacity:0将挡板进行消失,效果如下:

到此,我们已经实现绘制半个圆的动画效果,左边挡板的动画代码如下

  .left-baffle {
    width: px2vw(32);
    height: px2vw(64);
    position: absolute;
    background-color: yellow;
    left: 0;
    top: 0;
    opacity: 1;
    transform-origin: 100% 50%;
    animation: leftRotate 2s linear ;
    animation-fill-mode: forwards;
    z-index: 3;
  }
  @keyframes leftRotate {
  0% {
    transform: rotate(0);
  }
  99% {
    transform: rotate(-180deg);
    opacity: 1;
  }
  100% {
    transform: rotate(-180deg);
    opacity: 0;
  }
}

可能到这里有人已经注意到当左边的挡板消失后,右边的挡板开始旋转时,左边刚画好的圆会随着右边的挡板旋转而被覆盖,那右边的挡板怎样旋转才能做到既可以旋转起来又可以边旋转边“消失呢”?

当时我做到这里的时候想着,如果说右边的挡板在旋转过程能“藏”在左边的半圆下面,那不就直接起飞?

可能我说到这里有人觉得作者疯了,这怎么可能一张图片能拆成两张一张浮动在上面一张浮动在下面的。

那你别说还有这么个可能,这里我又利用了“障眼法”给实现了,我就不卖关子了直接整活。

实现右边挡板旋转的过程边“消失”需要以下两点:

  • 准备半个底图
  • 这半张个图的层级要高于右边的挡板
半张底图的位置(实际半张底图应该在左边挡板下面,为了方便理解底图的定位显示在外面):

首先半张底图我是这么实现的,取一个宽高跟挡板一样大的div,图片的宽高正常设置,超出div的部分设置overflow:hidden即可,半张底图有了之后,我们要将它定位在左边,为了层级高于右边挡板,我们将半张底图的层级设置为z-index:2,那为了使左边挡板一开始能够在半张底图上面,我们将左边挡板的层级设为z-index:3。

  <div class="dialog-success">
    <img src="../../images/icon_ok.png" class="icon-ok">
    <div class="left-baffle"></div>
    <div class="right-baffle"></div>
    <div class="harf-dialog-success">
      <img src="../../images/icon_ok.png" class="harf-icon-ok">
    </div>
  </div>
  .harf-dialog-success {
    width: px2vw(32);
    height: px2vw(64);
    position: absolute;
    z-index: 2;
    left: 0;
    top: 0;
    overflow: hidden;
    opacity: 1;
    .harf-icon-ok {
      width: auto;
      height: 100%;
    }
  }

完成以上两点之后我们再来看右边挡板旋转过程中的效果:

可以看到右边挡板已经被左边的半个底图给挡住了,将左右两边的挡板换成白色背景的即可

右边挡板动画代码如下:

由于当右边挡板旋转完之后接下来是画勾的动画,我们需要将左边的原有的半张底图同时给隐藏起来,由于半张底图隐藏之后会直接暴漏出右边底图,所以我们同时需要在右边挡板旋转到左边之后利用opacity:0一起隐藏起来。

  .right-baffle {
    width: px2vw(32);
    height: px2vw(64);
    position: absolute;
    background-color: white;
    right: 0;
    top: 0;
    opacity: 1;
    transform-origin:0 50%;
    animation: rightRotate 2s linear ;
    animation-delay: 2s;
    animation-fill-mode: forwards;
  }
  .harf-dialog-success {
    width: px2vw(32);
    height: px2vw(64);
    position: absolute;
    z-index: 2;
    left: 0;
    top: 0;
    overflow: hidden;
    opacity: 1;
    animation: harf-success 1s linear;
    animation-delay: 4s;
    animation-fill-mode: forwards;
    .harf-icon-ok {
      width: auto;
      height: 100%;
    }
  }
  @keyframes rightRotate {
  0% {
    transform: rotate(0);
  }
  99% {
    transform: rotate(-180deg);
    display: none;
    opacity: 1;
  }
  100% {
    transform: rotate(-180deg);
    opacity: 0;
  }
}
// 右边挡板旋转完之后立即隐藏,动画结束之前一直维持隐藏的状态
@keyframes harf-success {
  0% {
    opacity: 0;
  }
  1%,
  100% {
    opacity: 0;
  }
}

到这里我们已经成功将圆给画出来了,现在剩下的一个问题就是这个打勾“√”它的动画又是如何做到的。

划圆圈的过程“√”去哪了

值得我们注意的是,“√”打勾符号在画圈结束之前是一直不存在的,如何实现呢?

这里我们也是使用了障眼法的手段,给最外层的div标签添加了一个伪元素,背景为白色,定位在圆圈中间,大小正好能遮住打勾“√”符号即可。效果大致如下:

但是有个问题,我们之前用的那半张底图在画圈过程中也存在一部分“√”的图案,会导致在画圈过程中会露出一点图案出来。

同样的道理,我们利用伪元素将图案给遮住即可,去掉红色背景后整体效果如下:

整个过程打勾符号"√"其实就是这么隐藏起来的,剩下的最后一步就是将打勾符号“从左往右给”画出来。(从左往右是重点,要考的)

伪元素的代码如下:

   // 这是半张底图的伪元素 定位能够遮住图案就行了
  .harf-dialog-success::before {
    content: '';
    width: px2vw(14);
    height: px2vw(18);
    background-color: white;
    top: 50%;
    left: 60%;
    transform: translateY(-50%);
    position: absolute;
  }
  // 这是最外层div的伪元素 定位能够遮住图案就行了
  .dialog-success::before {
  content: '';
  width: px2vw(27);
  height: px2vw(27);
  position: absolute;
  background-color: white;
  top: 50%;
  left: 29%;
  transform: translateY(-50%);
}

打勾的动画是如何实现的

从最初始的动画可以看到打勾是从左到右开始的,介绍到这里作者把最后一个遇到的问题也给大家讲讲。本来实现打勾的效果就是利用伪元素的宽度从px2vw(27)开始到0即可,想法其实到这里已经差不多了,首先我把效果给大家呈上:

到这里一开始我是大失所望的,为什么跟我想的不一样?说好的宽度从左到右减少的呢?但仔细想想又给自己蠢哭了,浏览器在渲染dom元素不都一般是从左到右绘制的吗。。。

最后我采用的是将伪元素的定位改为right:29%,这样子能保证伪元素一直是往右定位的,即每次从右边减少多少宽度该伪元素也会因为right定位自动向右移动多少距离。

这样子整个过程的效果也就实现啦。

整个过程的代码如下:

  <div class="dialog-success" style="margin-top: 400px;">
    <img src="./icon_ok.png" class="icon-ok">
    <div class="left-baffle"></div>
    <div class="right-baffle"></div>
    <div class="harf-dialog-success">
      <img src="./icon_ok.png" class="harf-icon-ok">
    </div>
  </div>
  $design-width: 360; // 设计稿宽度360
  @function px2vw($size) {
    @return #{$size/$design-width * 100}vw;
  };
  .dialog-success {
    width: px2vw(64);
    height: px2vw(64);
    margin: 0 auto;
    position: relative;
    .left-baffle {
      width: px2vw(32);
      height: px2vw(64);
      position: absolute;
      background-color: white;
      left: 0;
      top: 0;
      opacity: 1;
      transform-origin: 100% 50%;
      animation: leftRotate 2s linear ;
      animation-fill-mode: forwards;
      z-index: 3;
    }
    .right-baffle {
      width: px2vw(32);
      height: px2vw(64);
      position: absolute;
      background-color: white;
      right: 0;
      top: 0;
      opacity: 1;
      transform-origin:0 50%;
      animation: rightRotate 2s linear ;
      animation-delay: 2s;
      animation-fill-mode: forwards;
    }
    .icon-ok {
      width: 100%;
      height: auto;
    }
    .harf-dialog-success {
      width: px2vw(32);
      height: px2vw(64);
      position: absolute;
      z-index: 2;
      left: 0;
      top: 0;
      overflow: hidden;
      opacity: 1;
      animation: harf-success 1s linear;
      animation-delay: 4s;
      animation-fill-mode: forwards;
      .harf-icon-ok {
        width: auto;
        height: 100%;
      }
    }
    .harf-dialog-success::before {
      content: '';
      width: px2vw(14);
      height: px2vw(18);
      background-color: white;
      top: 50%;
      left: 60%;
      transform: translateY(-50%);
      position: absolute;
    }
  }
  .dialog-success::before {
    content: '';
    width: px2vw(27);
    height: px2vw(27);
    position: absolute;
    background-color: white;
    top: 50%;
    right: 29%;
    transform: translateY(-50%);
    animation: tick 2s linear;
    animation-fill-mode: forwards;
    animation-delay: 4s;
  }


  @keyframes leftRotate {
    0% {
      transform: rotate(0);
    }
    99% {
      transform: rotate(-180deg);
      opacity: 1;
    }
    100% {
      transform: rotate(-180deg);
      opacity: 0;
    }
  }

  @keyframes rightRotate {
    0% {
      transform: rotate(0);
    }
    99% {
      transform: rotate(-180deg);
      display: none;
      opacity: 1;
    }
    100% {
      transform: rotate(-180deg);
      opacity: 0;
    }
  }

  @keyframes harf-success {
    0% {
      opacity: 0;
    }
    1%,
    100% {
      opacity: 0;
    }
  }

  @keyframes tick {
    0% {
      width: px2vw(27);
    }
    100% {
      width: 0;
    }
  }

结语

做一个动画要考虑的细节算是比较多吧。作者刚接触前端不久,对代码考虑的可能还不是很周全,如果文章有写错的或写得不好的地方请各位大佬随便指正。 源码地址:https://gitee.com/zhao_jiahao/css-animation