📒 背景

最近项目中需要制作一个图层拖拽到组的交互(如下图展示),今天分享一下这个组件功能。希望能抛砖引玉,给大家带来启发。

基于vue制作的动画组件loading起来_lottie


🔍需求功能

制作动画,提升用户体验。比如在页面加载的前,比如提交的时候,比如上传的时候,都需要一个酷炫的动画来提升用户的体验。


👣设计开发

先说一下我的开发环境版本:

基于vue制作的动画组件loading起来_loading_02

node: v11.3.0

npm: 6.4.1

vue:2.5.11

如果不是以上版本也没关系,今日分享的思路,相信你可以自己造出来~


首先我们都知道web端的动画,分为CSS制作的,JS制作的,还有GIF图片的。今天我们主要说以下三种:

1.PNG序列帧:

基于vue制作的动画组件loading起来_Data_03

顾名思义,一张透明的png图片,每一帧放一格铺满多格,然后做动画。

<div class="loadingBG">
		<div class="loadingbox">
			<div class="templateLoading">
				<img :src="imgurl" class="img">
			</div>
			<p>{{loadingText}}</p>
		</div>
	</div>
.loadingbox{
		position: relative;
		margin-left: 50%;
		transform: translateX(-50%) translateY(30vh);
		text-align: center;
		width: 340px;
		display: flex;
		flex-direction: column;
		align-items: center;
	}
	@keyframes loadingImg {
		0%{
			transform: translateX(0);
		}
		100%{
			transform: translateX(-1440px);
		}
	}
.templateLoading{
	width: 160px;
	height: 160px;
	overflow: hidden;
	pointer-events: none;
	.img{
		width: 1440px;
		height: 160px;
		animation: 1800ms steps(9) 0s infinite normal none running loadingImg;
	}
}

animation,考虑我这个只做9帧,每步播放。

animation: 1800ms steps(9) 0s infinite normal none running loadingImg;

封装成组件,则想传入加载提示文案:loadingText

最终效果:

基于vue制作的动画组件loading起来_loading_04

2.lottie动画:

最近看iconfont多了一个lottie动效库,使用起来动画很丝滑。

基于vue制作的动画组件loading起来_lottie_05

简单讲一下使用步骤:

vue2项目npm install lottie-web

然后,子页面封装:

intLottie () {
      const { animationData, autoplay, loop } = this
      this.lottie = lottie.loadAnimation({
        container: this.$el, // 渲染容器
        renderer: 'svg', // 渲染方式 svg|canvas|html
        loop, // 是否循环 true|false|number
        autoplay, // 自动播放 true|false
        animationData // lottie json文件
      })
    },

父页面引入,传入json:

 <lottie :animationData="lottie" :autoplay="true"></lottie>

……


lottie: require('../assets/lottie/car-loading.json')

很好用的是,可以设置鼠标移入移除动画的交互倒放,这就很nice,体验感满满。

 <div class="lottie"  @mouseenter="onMouseenter" @mouseleave="onMouseleave"></div>

onMouseenter () {
      // 我这里demo,非自动播放则判断为鼠标交互播放,具体封装根据自身业务场景来
      if (!this.autoplay) {
        this.lottie.setDirection(-1) // 回退
        this.lottie.play()
        // 监听lottie播放结束
        this.lottie.addEventListener('complete', e => {
          this.lottie.stop()
        })
      }
    },
    onMouseleave () {
      this.lottie.removeEventListener('complete')
      if (!this.autoplay) {
      	// 改变播放方向,鼠标移出继续
        this.lottie.setDirection(1)
      }
    }

最终效果:

基于vue制作的动画组件loading起来_Data_06


3.SVG+CSS动画:

svg这种方式就很传统,把svg当成不同部分的div组成,再分别加动画。

<template>
  <svg t="1586507018901" class="icon img04" viewBox="0 0 1142 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10824" width="555" height="555">
      <g id="svg-path-yuan"><path d="M0 537.307985a166.448669 163.528517 0 1 0 332.897338 0 166.448669 163.528517 0 1 0-332.897338 0Z" fill="#FFD943" p-id="10825"></path><path d="M33.474677 536.379376a132.043437 129.15054 0 1 0 264.086874 0 132.043437 129.15054 0 1 0-264.086874 0Z" fill="#FFB813" p-id="10826"></path><path d="M141.393764 455.543726c1.111605 0 2.129764 0.617125 2.635924 1.598297l23.341749 45.287665 23.343696-45.287665a2.959087 2.959087 0 0 1 2.36143-1.584669L193.353004 455.543726h17.010859c1.02984 0 1.985703 0.529521 2.523012 1.39778a2.900684 2.900684 0 0 1 0.114859 2.857855L190.993521 502.513399h20.869354c1.319909 0 2.390631 1.070722 2.390631 2.392578v18.792152c0 1.321856-1.070722 2.392578-2.392578 2.392578l-33.139833-0.001947v23.209369h33.139833c1.321856 0 2.394525 1.070722 2.394524 2.394525v18.792152c0 1.319909-1.072669 2.390631-2.394524 2.390631H178.721095v44.041734c0 1.615817-1.333536 2.927939-2.978555 2.927939h-17.857703a2.953247 2.953247 0 0 1-2.978556-2.927939V572.875437H121.768395c-1.319909 0-2.390631-1.070722-2.390631-2.390631v-18.794099c0-1.319909 1.070722-2.392578 2.390631-2.392578h33.14178V526.08876H121.766449c-1.319909 0-2.390631-1.070722-2.390632-2.390631v-18.792152c0-1.321856 1.070722-2.392578 2.390632-2.392578h21.984851L121.743087 459.799361a2.900684 2.900684 0 0 1 0.11486-2.857855A2.966875 2.966875 0 0 1 124.379011 455.543726h17.01086z" fill="#FFFFFF" p-id="10827"></path></g>
      <g id="svg-path-drop" style="filter: url('#goo');">
          <defs>
              <filter id="goo">
                  <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
                  <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 19 -9" result="goo" />
                  <feBlend in="SourceGraphic" in2="goo" />
              </filter>
          </defs>
          <circle class="flyCircle" cx="250" cy="125" r="40" style="fill: hsla(222, 100%, 84%, 1);"></circle>
          <circle class="flyCircle2" cx="280" cy="105" r="40" style="fill:hsla(222, 100%, 84%, 1);"></circle>
      </g>
      <g id="svg-path-drop2" style="filter: url('#goo');">
          <defs>
              <filter id="goo">
                  <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
                  <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 19 -9" result="goo" />
                  <feBlend in="SourceGraphic" in2="goo" />
              </filter>
          </defs>
          <circle class="flyCircle" cx="180" cy="840" r="50" style="fill: hsla(222, 100%, 84%, 1);"></circle>
          <circle class="flyCircle2" cx="220" cy="865" r="50" style="fill:hsla(222, 100%, 84%, 1);"></circle>
      </g>
      <!-- <path d="M212.933597 778.707224c25.134722 0 45.511544 20.79927 45.511544 46.457673 0 1.886418-0.110966 3.745582-0.325111 5.573597 8.328274 74.851285 81.316502 95.3527 107.804229 100.353947l-0.667742-0.00584c25.134722 0 45.511544 20.79927 45.511544 46.455726 0 25.658403-20.376821 46.457673-45.511544 46.457673-25.01597 0-45.316867-20.6007-45.509597-46.089734-20.131529-80.833703-65.639179-102.925627-91.854357-108.859376a44.557627 44.557627 0 0 1-14.958966 2.569734C187.798875 871.620624 167.422053 850.821354 167.422053 825.164897 167.422053 799.506494 187.798875 778.707224 212.933597 778.707224zM358.285141 57.480274c13.296426 13.892137 12.796106 35.917871-1.121339 49.192882a34.777065 34.777065 0 0 1-25.047118 9.622874c-68.66251 15.591665-82.901171 56.255757-85.805749 74.444411 0.402981 2.242677 0.587924 4.559331 0.533415 6.926601-0.438023 19.20876-16.391787 34.426646-35.633643 33.99057-19.241856-0.438023-34.483103-16.366479-34.04508-35.575239 0.436076-19.086114 16.18543-34.23197 35.259863-33.996411l-0.615178-0.694996c76.439848-1.193369 86.773293-88.50397 86.773293-88.50397l0.597657 0.574297a34.61743 34.61743 0 0 1 9.831179-17.102358c13.917445-13.275011 35.976274-12.770798 49.2727 1.121339z" fill="#ADC6FF" opacity=".298" p-id="10828"></path>-->
      <g id="svg-path-circle3"><path d="M643.266677 0.161582c13.89019 1.594403 23.906312 14.581293 22.372258 29.006844-1.534053 14.425551-14.040091 24.827133-27.930281 23.23273-13.892137-1.592456-23.910205-14.581293-22.374205-29.006844 1.534053-14.425551 14.040091-24.827133 27.932228-23.23273" fill="#597EF7" opacity=".3" p-id="10829"></path></g>
      <g id="svg-path-lightning"><path d="M812.390205 117.32003H417.998357c-12.829202 0-23.540319 9.574205-25.337186 22.337217-11.723437 83.296365-87.29308 126.119422-120.300532 140.562494A25.668137 25.668137 0 0 0 256.973384 303.7367v338.957627c0 221.8167 358.220897 363.157901 358.220897 363.157901s358.218951-141.341202 358.218951-363.157901V303.734753c0-10.220532-6.060289-19.434586-15.387255-23.515012-33.005506-14.443072-108.577095-57.266129-120.300532-140.562494-1.796867-12.763011-12.507985-21.268441-25.33524-21.268441v-1.070722z" fill="#C3D9EE" p-id="10830"></path><path d="M779.83051 183.531559a220.582449 220.582449 0 0 0 19.434585 43.549201c11.894753 20.429384 27.291741 39.472669 45.76073 56.594495 20.711665 19.204867 42.601125 33.570068 61.624943 43.954129V640.934449c0 84.807057-80.654601 159.923103-148.312578 208.002433-55.003985 39.085262-111.024183 67.177125-142.230874 81.550114-30.700532-14.114068-85.468958-41.559605-140.16146-80.148441-46.274677-32.651194-82.655878-66.334175-108.131286-100.114494-13.868776-18.391118-24.451407-36.832852-31.457825-54.815148-7.203042-18.48651-10.703331-36.305278-10.703331-54.474464V327.629384c19.021871-10.384061 40.915224-24.751209 61.62689-43.954129 18.467042-17.121825 33.86403-36.163163 45.758783-56.592548a220.619437 220.619437 0 0 0 19.436533-43.551148h327.35489" fill="#0062B2" p-id="10831"></path><path d="M633.634068 362.163103l-118.071482 179.747041c-6.350357 9.745521 0.478905 21.729825 11.933688 21.729826h52.885901l-65.025947 122.780714c-4.851346 9.922677 7.650798 19.132837 15.315224 11.281521l176.854145-177.611437c9.025217-9.241308 2.624243-25.029597-10.146555-25.029597h-79.977126l33.457156-125.095422c2.612563-10.648821-11.271787-16.936882-17.225004-7.802646" fill="#FFFFFF" p-id="10832"></path></g>
      <g id="svg-path-rect"  style="filter: url('#goo');">
          <!--<defs>
              <filter id="dropshadow" x="0" y="0" width="200%" height="200%">
                  <feOffset result="offsetResult" in="SourceAlpha" dx="20" dy="20" />
                  <feGaussianBlur result="blurResult" in="offsetResult" stdDeviation="5" />
                  <feBlend in="SourceGraphic" in2="blurResult" mode="normal" />
              </filter>
          </defs>
          <rect width="50" height="50" fill="#597EF7" filter="url(#dropshadow)" />-->
          <path d="M1122.113217 505.379042L1091.889643 535.945247c-3.809825 3.854601-10.020015 3.879909-13.798692 0.058403l-30.336487-30.681064c-3.778677-3.823452-3.753369-10.103726 0.058403-13.956381l30.221627-30.566205c3.809825-3.854601 10.020015-3.879909 13.798692-0.058403l30.336487 30.681065c3.778677 3.823452 3.753369 10.103726-0.056456 13.95638" fill="#597EF7" opacity=".21" p-id="10833"></path></g>
      <g id="svg-path-yuangou"><path d="M912.060837 58.403042c-86.551361 0-156.714829 70.599544-156.714829 157.688213s70.163468 157.688213 156.714829 157.688213 156.714829-70.599544 156.714828-157.688213-70.163468-157.688213-156.714828-157.688213" fill="#39B990" p-id="10834"></path><path d="M885.861232 267.427529l-9.965506-9.920731 96.565536-96.46041a8.484015 8.484015 0 0 1 11.95705 0l3.986981 3.967513a8.388624 8.388624 0 0 1 0 11.90254l-90.587012 90.509141a8.484015 8.484015 0 0 1-11.957049 0" fill="#FFFFFF" p-id="10835"></path><path d="M909.278905 259.173232l-10.093992 10.070631a8.579407 8.579407 0 0 1-12.112791 0l-40.19492-40.103422a8.530738 8.530738 0 0 1 0-12.083589l4.03565-4.02981a8.579407 8.579407 0 0 1 12.114737 0l46.251316 46.144243z" fill="#FFFFFF" p-id="10836"></path></g>
      <g id="svg-path-window"><path d="M1141.249947 642.458768H755.346008V609.130099C755.346008 601.901749 761.427711 596.041977 768.928608 596.041977h358.740685c7.500897 0 13.580654 5.859772 13.580654 13.090069v33.328669z" fill="#18273A" p-id="10837"></path><path d="M1127.669293 978.511817H768.928608c-7.500897 0-13.582601-6.132319-13.5826-13.69746V638.645049h385.903939v326.169308c0 7.565141-6.079757 13.69746-13.580654 13.69746" fill="#E4EBF7" p-id="10838"></path><path d="M786.858342 619.802281a7.413293 7.413293 0 1 1-14.828532-0.001946 7.413293 7.413293 0 0 1 14.828532 0.001946" fill="#FF7946" p-id="10839"></path><path d="M818.36873 619.802281a7.413293 7.413293 0 1 1-14.826586-0.001946 7.413293 7.413293 0 0 1 14.826586 0.001946" fill="#FFC53D" p-id="10840"></path><path d="M848.027741 619.802281a7.413293 7.413293 0 1 1-14.828532-0.001946 7.413293 7.413293 0 0 1 14.828532 0.001946" fill="#BAE637" p-id="10841"></path><path d="M796.656426 929.281947a4.236167 4.236167 0 0 1-4.238114-4.232274V679.630357c0-2.336122 1.896152-4.232274 4.238114-4.232273h306.639331c2.340015 0 4.238114 1.894205 4.238114 4.232273v245.419316c0 2.336122-1.898099 4.232274-4.238114 4.232274H796.656426z" fill="#FFFFFF" p-id="10842"></path><path d="M901.69819 710.609278h-75.830509c-3.105095 0-5.645627-2.503544-5.645628-5.561917 0-3.056426 2.540532-5.558023 5.645628-5.558023h75.830509c3.105095 0 5.645627 2.501597 5.645627 5.558023 0 3.058373-2.540532 5.55997-5.645627 5.55997M857.031544 738.405232H827.902053c-3.216061 0-5.82473-2.489916-5.82473-5.55997s2.608669-5.558023 5.82473-5.558022h29.129491c3.216061 0 5.82473 2.48797 5.82473 5.558022s-2.608669 5.55997-5.82473 5.55997" fill="#CCD7EE" p-id="10843"></path></g>
  </svg>
</template>

<script>
  export default {
      name: "svgLoading"
  }
</script>

<style scoped>
  @keyframes updown {
      0% {
          transform: translateY(-5%);
      }
      50% {
          transform: translateY(5%);
      }
      100%{
          transform: translateY(-5%);
      }
  }
  @keyframes leftright {
      0% {
          transform: translateX(-15%);
      }
      50% {
          transform: translateX(-5%);
      }
      100%{
          transform: translateX(-15%);
      }
  }
  @keyframes transformdemo {
      0% {
          transform: translate(5%,0);
      }
      50% {
          transform: translate(0,5%);
      }
      100%{
          transform: translate(5%,0);
      }
  }
  @keyframes transformdemo2 {
      0% {
          transform: translate(0,5%);
      }
      50% {
          transform: translate(5%,0);
      }
      100%{
          transform: translate(0,5%);
      }
  }
  @keyframes transformdemo3 {
      0% {
          transform: translate(5%,5%);
      }
      50% {
          transform: translate(10%,10%);
      }
      100%{
          transform: translate(5%,5%);
      }
  }
  @keyframes transformdemo4 {
      0% {
          transform: translate(10%,10%);
      }
      50% {
          transform: translate(5%,5%);
      }
      100%{
          transform: translate(10%,10%);
      }
  }
  @keyframes scale {
      0% {
          transform: scale(1);
      }
      50% {
          transform: scale(1.2);
      }
      100%{
          transform: scale(1);
      }
  }
  @keyframes scalehight {
      0% {
          transform: scale(0.6);
      }
      50% {
          transform: scale(1);
      }
      100%{
          transform: scale(0.6);
      }
  }
  #svg-path-lightning{
      animation: updown 5s linear infinite;
  }
  #svg-path-window{
      animation: leftright 5s linear infinite;
  }
  #svg-path-yuan{
      animation: scale 5s linear infinite;
  }
  #svg-path-yuangou{
      transform-origin: center;
      animation: scalehight 3s linear infinite;
  }
  #svg-path-drop .flyCircle{
      animation: transformdemo 5s linear infinite;
  }
  #svg-path-drop .flyCircle2{
      animation: transformdemo2 5s linear infinite;
  }
  #svg-path-drop2 .flyCircle{
      animation: transformdemo3 5s linear infinite;
  }
  #svg-path-drop2 .flyCircle2{
      animation: transformdemo4 5s linear infinite;
  }
  #svg-path-circle3{
      animation: scale 5s linear infinite;
  }
  #svg-path-rect{
      animation: updown 4s linear infinite;
  }
</style>

其中主要是有点黏黏糊糊的效果,加上filter:

<g id="svg-path-drop2" style="filter: url('#goo');">
          <defs>
              <filter id="goo">
                  <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
                  <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 19 -9" result="goo" />
                  <feBlend in="SourceGraphic" in2="goo" />
              </filter>
          </defs>
          <circle class="flyCircle" cx="180" cy="840" r="50" style="fill: hsla(222, 100%, 84%, 1);"></circle>
          <circle class="flyCircle2" cx="220" cy="865" r="50" style="fill:hsla(222, 100%, 84%, 1);"></circle>
      </g>

最终效果:

基于vue制作的动画组件loading起来_json_07


更多逻辑代码欢迎体验组件--mycomponentsvue

npm install mycomponentsvue@0.0.17

import mycomponents from 'mycomponentsvue'

Vue.use(mycomponents)

<gifLoading :loadingText="loadingText"></gifLoading>
  <!-- <lottie :animationData="lottie" :autoplay="false"></lottie> -->
  <!-- <svgLoading style="width: 100px;"></svgLoading> -->

本组件只用于学习交流哈!所以名字起的比较随意!~


⛳参考学习

iconfont的lottie动效库使用: https://www.iconfont.cn/

基于vue制作的动画组件loading起来_loading_08


🚀写在最后

如果本文中有bug、逻辑错误,或者您有更好的优化方案欢迎评论联系我哦!~关注我持续分享日常工作中的组件设计和学习分享,一起进步加油!