自定义loading指令

165 阅读10分钟

1、前言

区域加载loading / 骨架屏,这一些用于用户等待数据的场景下的交互动作,是web性能优化的一个环节。下文主要讲解区域加载loading , 这里以element-ui组件的自定义指令v-loading ,自定义封装自定义指令(lottie 动画 / css样式)

源码地址:gitee.com/acher_Saber…

2、element-ui的v-loading使用

这里不做过多的讲解,代码示例如下:

  <MyButton
     v-loading.fullscreen.lock="fullscreenLoading"
     type="primary"
     @click="openFullScreen1"
  >
     全屏指令
  </MyButton>

  <MyButton type="primary" @click="openFullScreen2">
    全屏自定义指令
  </MyButton>
  
    // 全局loading
    openFullScreen1 () {
      this.fullscreenLoading = true
      setTimeout(() => {
        this.fullscreenLoading = false
      }, 2000)
    },
    openFullScreen2 () {
      const loading = this.$loading({
        lock: true,
        text: 'Loading',
        spinner: 'el-icon-loading',
        background: 'rgba(0, 0, 0, 0.7)'
      })
      setTimeout(() => {
        loading.close()
      }, 2000)
    },

3、自定义v-myLoading

9.gif

  • MyLoading.vue封装组件
<!--
 * @Author: Null
 * @Date: 2022-03-22 13:56:30
 * @Description: MyLoading组件
-->

<template>
  <div class="MyLoading" flex="main:center cross:center" v-on="$listeners">
    <div v-if="customizedAttrs.loadingType === 'Fade-Circle'" class="loader-Fade-Circle" />
    <div v-else-if="customizedAttrs.loadingType === '3D-Flip'" class="loader-3D-Flip" />
    <div v-else-if="customizedAttrs.loadingType === 'Surround-Slice'" class="loader-Surround-Slice" />
    <div v-else-if="customizedAttrs.loadingType === 'Surround-Circle'" class="loader-Surround-Circle" />
    <!-- 默认加载loading -->
    <div v-else-if="customizedAttrs.loadingType === 'default'" class="loader-default">
      <div class="my-loading-loader">
        <div class="my-loading" />
        <div class="my-loading-text">加载中...</div>
      </div>
    </div>
  </div>
</template>

<script>
import { mapState } from 'vuex'
export default {
  name: 'MyLoading',
  inheritAttrs: false,
  props: {
    loadingType: {
      type: String,
      default: ''
    }
  },
  computed: {
    ...mapState('element/myLoading', ['currentSelectedLoadingType']),
    // Fade-Circle 渐隐圆环 ; 3D-Flip 翻转; Surround-Slice 环绕切片; Surround-Circle 环绕圆
    customizedAttrs () {
      if (this.loadingType) {
        return {
          loadingType: this.loadingType
        }
      } else {
        return {
          loadingType: this.currentSelectedLoadingType || 'default',
          // 支持传过来的size覆盖默认的size
          ...this.$attrs
        }
      }
    }
  }
}
</script>

<style lang="scss">
.MyLoading {
  position: relative;
  width: 100%;
  min-height: 100px;
  border-radius: 3px;
  font-size: 48px;
  color: $color-primary;
  padding: 1em;
  margin-bottom: 0.15em;
  vertical-align: top;
  -webkit-transition: 0.3s color, 0.3s border;
  transition: 0.3s color, 0.3s border;
}
[class*='loader-'] {
  display: inline-block;
  width: 1em;
  height: 1em;
  color: inherit;
  vertical-align: middle;
  pointer-events: none;
}

.loader-Fade-Circle {
  position: relative;
}
.loader-Fade-Circle:before,
.loader-Fade-Circle:after {
  content: '';
  width: inherit;
  height: inherit;
  border-radius: 50%;
  background-color: currentcolor;
  opacity: 0.6;
  position: absolute;
  top: 0;
  left: 0;
  -webkit-animation: loader-Fade-Circle 2s infinite ease-in-out;
  animation: loader-Fade-Circle 2s infinite ease-in-out;
}
.loader-Fade-Circle:after {
  -webkit-animation-delay: -1s;
  animation-delay: -1s;
}
@-webkit-keyframes loader-Fade-Circle {
  0%,
  100% {
    -webkit-transform: scale(0);
    transform: scale(0);
  }
  50% {
    -webkit-transform: scale(1);
    transform: scale(1);
  }
}
@keyframes loader-Fade-Circle {
  0%,
  100% {
    -webkit-transform: scale(0);
    transform: scale(0);
  }
  50% {
    -webkit-transform: scale(1);
    transform: scale(1);
  }
}

.loader-3D-Flip {
  background-color: currentcolor;
  border-radius: 8px;
  background: url('../../assets/images/logo.png') center no-repeat;
  background-size: 100% 100%;
  -webkit-animation: loader-3D-Flip 1.2s infinite ease-in-out;
  animation: loader-3D-Flip 1.2s infinite ease-in-out;
}
@-webkit-keyframes loader-3D-Flip {
  0% {
    -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg);
    transform: perspective(120px) rotateX(0deg) rotateY(0deg);
  }
  50% {
    -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
    transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
  }
  100% {
    -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
    transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
  }
}
@keyframes loader-3D-Flip {
  0% {
    -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg);
    transform: perspective(120px) rotateX(0deg) rotateY(0deg);
  }
  50% {
    -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
    transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
  }
  100% {
    -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
    transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
  }
}

.loader-Surround-Slice {
  -webkit-transform: rotateZ(45deg);
  transform: rotateZ(45deg);
  -webkit-perspective: 1000px;
  perspective: 1000px;
  border-radius: 50%;
}
.loader-Surround-Slice:before,
.loader-Surround-Slice:after {
  content: '';
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  width: inherit;
  height: inherit;
  border-radius: 50%;
  -webkit-animation: 1s spin linear infinite;
  animation: 1s spin linear infinite;
}
.loader-Surround-Slice:before {
  -webkit-transform: rotateX(70deg);
  transform: rotateX(70deg);
}
.loader-Surround-Slice:after {
  -webkit-transform: rotateY(70deg);
  transform: rotateY(70deg);
  -webkit-animation-delay: 0.4s;
  animation-delay: 0.4s;
}
@-webkit-keyframes spin {
  0%,
  100% {
    box-shadow: 0.2em 0px 0 0px currentcolor;
  }
  12% {
    box-shadow: 0.2em 0.2em 0 0 currentcolor;
  }
  25% {
    box-shadow: 0 0.2em 0 0px currentcolor;
  }
  37% {
    box-shadow: -0.2em 0.2em 0 0 currentcolor;
  }
  50% {
    box-shadow: -0.2em 0 0 0 currentcolor;
  }
  62% {
    box-shadow: -0.2em -0.2em 0 0 currentcolor;
  }
  75% {
    box-shadow: 0px -0.2em 0 0 currentcolor;
  }
  87% {
    box-shadow: 0.2em -0.2em 0 0 currentcolor;
  }
}
@keyframes spin {
  0%,
  100% {
    box-shadow: 0.2em 0px 0 0px currentcolor;
  }
  12% {
    box-shadow: 0.2em 0.2em 0 0 currentcolor;
  }
  25% {
    box-shadow: 0 0.2em 0 0px currentcolor;
  }
  37% {
    box-shadow: -0.2em 0.2em 0 0 currentcolor;
  }
  50% {
    box-shadow: -0.2em 0 0 0 currentcolor;
  }
  62% {
    box-shadow: -0.2em -0.2em 0 0 currentcolor;
  }
  75% {
    box-shadow: 0px -0.2em 0 0 currentcolor;
  }
  87% {
    box-shadow: 0.2em -0.2em 0 0 currentcolor;
  }
}

.loader-Surround-Circle {
  position: relative;
  background-color: currentcolor;
  border-radius: 50%;
}
.loader-Surround-Circle:after,
.loader-Surround-Circle:before {
  content: '';
  position: absolute;
  width: 0.25em;
  height: 0.25em;
  border-radius: 50%;
  opacity: 0.8;
}
.loader-Surround-Circle:after {
  left: -0.5em;
  top: -0.25em;
  background-color: currentcolor;
  -webkit-transform-origin: 0.75em 1em;
  transform-origin: 0.75em 1em;
  -webkit-animation: loader-Surround-Circle 1s linear infinite;
  animation: loader-Surround-Circle 1s linear infinite;
  opacity: 0.6;
}
.loader-Surround-Circle:before {
  left: -1.25em;
  top: -0.75em;
  background-color: currentcolor;
  -webkit-transform-origin: 1.5em 1em;
  transform-origin: 1.5em 1em;
  -webkit-animation: loader-Surround-Circle 2s linear infinite;
  animation: loader-Surround-Circle 2s linear infinite;
}
@-webkit-keyframes loader-Surround-Circle {
  0% {
    -webkit-transform: rotateZ(0deg) translate3d(0, 0, 0);
    transform: rotateZ(0deg) translate3d(0, 0, 0);
  }
  100% {
    -webkit-transform: rotateZ(360deg) translate3d(0, 0, 0);
    transform: rotateZ(360deg) translate3d(0, 0, 0);
  }
}
@keyframes loader-Surround-Circle {
  0% {
    -webkit-transform: rotateZ(0deg) translate3d(0, 0, 0);
    transform: rotateZ(0deg) translate3d(0, 0, 0);
  }
  100% {
    -webkit-transform: rotateZ(360deg) translate3d(0, 0, 0);
    transform: rotateZ(360deg) translate3d(0, 0, 0);
  }
}

/* 默认加载动画 */
.my-loading-loader {
  position: relative;
  font-size: 12px;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 60px;
}

.my-loading-loader .my-loading {
  position: relative;
  width: 100%;
  height: 10px;
  border: 1px solid $color-primary;
  border-radius: 10px;
  animation: turnDefault 1s linear 0.42s infinite;
}

.my-loading-loader .my-loading:before {
  content: "";
  display: block;
  position: absolute;
  width: 0%;
  height: 100%;
  background: $color-primary;
  box-shadow: 10px 0px 15px 0px $color-primary;
  animation: loadDefault .5s linear infinite;
}

.my-loading-loader .my-loading-text {
  width: 100%;
  position: absolute;
  top: 10px;
  color: $color-primary;
  text-align: center;
  animation: bounceDefault .5s linear infinite;
}

@keyframes loadDefault {
  0% {
    width: 0%;
  }

  87.5%,
  100% {
    width: 100%;
  }
}

@keyframes turnDefault {
  0% {
    transform: rotateY(0deg);
  }

  6.25%,
  50% {
    transform: rotateY(180deg);
  }

  56.25%,
  100% {
    transform: rotateY(360deg);
  }
}

@keyframes bounceDefault {
  0%,
  100% {
    top: 10px;
  }

  12.5% {
    top: 30px;
  }
}
/* 默认加载动画 */
</style>

  • 页面中使用
      <div class="MyLoading-display">
        <MyLoading />
        <MyLoading loading-type="3D-Flip" />
        <MyLoading loading-type="Surround-Slice" />
        <MyLoading loading-type="Surround-Circle" />
        <MyLoading loading-type="default" />
      </div>

3、自定义v-myLoading

  • CSS的loading

10.gif

  • vue-lottie安装
npm install vue-lottie
yarn add vue-lottie
"vue-lottie": "^0.2.1",
  • MyLottie组件封装

3655b2dce2f7bf55168802f098226c7.jpg

【注意,这里的lottie的动画模型,需要在lottie官网中去下载,也可以去我的源码内拿取】

MyLottie/index.vue 文件

<!--
 * @Author: Null
 * @Date: 2022-03-28 17:45:27
 * @Description: lottie动画
-->
<template>
  <div class="MyLottie" flex="main:center cross:center">
    <VueLottie v-bind="VueLottieOptions" @animCreated="handleAnimation" />
  </div>
</template>

<script>
import Loading0 from './lottieModel/loading0.json'
import Loading1 from './lottieModel/loading1.json'
import Loading2 from './lottieModel/loading2.json'
import Loading3 from './lottieModel/loading3.json'
import Loading4 from './lottieModel/loading4.json'

export default {
  name: 'MyLottie',
  props: {
    lottieType: {
      type: String,
      default: 'loading0'
    }
  },
  data () {
    return {
      anim1: {},
      VueLottieOptions: {
        height: 140,
        width: 140,
        options: {
          animationData: Loading0
        }
      }
    }
  },
  created () {
    this.judgeLottieType(this.lottieType)
  },
  methods: {
    handleAnimation (anim) {
      this.anim1 = anim
    },
    // 判断动画json文件
    judgeLottieType (type) {
      let loadingType = Loading0
      if (type === 'loading0') {
        loadingType = Loading0
      } else if (type === 'loading1') {
        loadingType = Loading1
      } else if (type === 'loading2') {
        loadingType = Loading2
      } else if (type === 'loading3') {
        loadingType = Loading3
      } else if (type === 'loading4') {
        loadingType = Loading4
      }

      this.VueLottieOptions.options.animationData = loadingType
    }
  }
}
</script>

<style lang="scss" scoped>
.MyLottie {
  width: 100%;
  height: 100%;
}
</style>

MyLottie/lottieModel/loading1.json 文件

{"v":"4.10.1","fr":24,"ip":0,"op":72,"w":400,"h":400,"nm":"Comp 1","ddd":0,"assets":[{"id":"comp_0","layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Capa de formas 12","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":24,"s":[81.5,370.25,0],"e":[445.5,199.25,0],"to":[60.6666679382324,-28.5,0],"ti":[-60.6666679382324,28.5,0]},{"t":48}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[43,43,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[28,0],[34.935,-19.483],[31.619,18.821],[33,-14],[57,29],[0,0],[0,0],[0,0]],"o":[[-28,0],[-52,29],[-42,-25],[-28.892,12.257],[-57,-29],[0,0],[0,0],[0,0]],"v":[[367.75,-97],[277,-75],[155,-82],[35,-82],[-94,-82.326],[-200,-74],[-352.07,320.209],[499.162,354.093]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2980392156862745,0.7333333333333333,0.6313725490196078,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformar"}],"nm":"Forma 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":144,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Capa de formas 2","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":24,"s":[-133,374,0],"e":[231,203,0],"to":[60.6666679382324,-28.5,0],"ti":[-60.6666679382324,28.5,0]},{"t":48}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[43,43,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[28,0],[34.935,-19.483],[31.619,18.821],[33,-14],[57,29],[0,0],[0,0],[0,0]],"o":[[-28,0],[-52,29],[-42,-25],[-28.892,12.257],[-57,-29],[0,0],[0,0],[0,0]],"v":[[367.75,-97],[277,-75],[155,-82],[35,-82],[-94,-82.326],[-200,-74],[-352.07,320.209],[499.162,354.093]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.3137254901960784,0.8901960784313725,0.7607843137254902,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformar"}],"nm":"Forma 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":144,"st":0,"bm":0}]}],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Capa de formas 5","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":15,"s":[100],"e":[0]},{"t":16}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":0,"s":[199,-14.000000000000002,0],"e":[199,156,0],"to":[0,28.3333339691162,0],"ti":[0,-28.9375,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":12,"s":[199,156,0],"e":[199,164.066,0],"to":[0,4.54861259460449,0],"ti":[0,-2.45892143249512,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":13,"s":[199,164.066,0],"e":[199,166.125,0],"to":[0,13.1843204498291,0],"ti":[0,-1.72074222564697,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":14,"s":[199,166.125,0],"e":[199,168.375,0],"to":[0,2.04166674613953,0],"ti":[0,-0.04166666790843,0]},{"t":15}],"ix":2},"a":{"a":0,"k":[-1,-182.375,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":0,"s":[50,50,100],"e":[50,94,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":12,"s":[50,94,100],"e":[70,43.333,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":13,"s":[70,43.333,100],"e":[104.25800000000001,32,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":14,"s":[104.25800000000001,32,100],"e":[212,18,100]},{"t":15}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.938,0],[0,-5.25],[-4.563,0.125],[0.108,4.624]],"o":[[-0.813,0.125],[0,4.813],[4.563,-0.125],[-0.125,-5.375]],"v":[[-1.344,-193.078],[-8.75,-180.5],[-1.063,-172.313],[6.938,-180.188]],"c":true},"ix":2},"nm":"Trazado 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2980392156862745,0.7333333333333333,0.6313725490196078,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformar"}],"nm":"Forma 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Capa de formas 3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":46,"s":[0],"e":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":47,"s":[100],"e":[100]},{"t":48}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":47,"s":[199.98,168.25,0],"e":[199.98,158.037,0],"to":[0,-0.20375619828701,0],"ti":[0,17.58864402771,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"n":"0p833_0p833_0p167_0p167","t":48,"s":[199.98,158.037,0],"e":[199.98,-10,0],"to":[-2.8421709430404e-14,-50.4047393798828,0],"ti":[0,1.17485654354095,0]},{"t":53}],"ix":2},"a":{"a":0,"k":[-32,-31,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":47,"s":[-4,1,100],"e":[1.5,4,100]},{"i":{"x":[0.833,0.833,0.833],"y":[0.833,0.833,0.833]},"o":{"x":[0.167,0.167,0.167],"y":[0.167,0.167,0.167]},"n":["0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167","0p833_0p833_0p167_0p167"],"t":48,"s":[1.5,4,100],"e":[2,3,100]},{"t":53}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[308,308],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Trazado elíptico 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2980392156862745,0.7333333333333333,0.6313725490196078,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-31,-31],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformar"}],"nm":"Elipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"Capa de formas 4","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[199,252.99999999999997,0],"ix":2},"a":{"a":0,"k":[-32,-31,0],"ix":1},"s":{"a":0,"k":[55.00000000000001,55.00000000000001,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[308,308],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Trazado elíptico 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.2980392156862745,0.7333333333333333,0.6313725490196078,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"nm":"Trazo 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-31,-31],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformar"}],"nm":"Elipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":12,"s":[50],"e":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":24,"s":[100],"e":[50]},{"t":48}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":12,"s":[50],"e":[0]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":24,"s":[0],"e":[50]},{"t":48}],"ix":2},"o":{"a":0,"k":180,"ix":3},"m":1,"ix":2,"nm":"Recortar trazados 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Capa de formas 1","td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[199,252.99999999999997,0],"ix":2},"a":{"a":0,"k":[-32,-31,0],"ix":1},"s":{"a":0,"k":[50,50,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[308,308],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Trazado elíptico 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2980392156862745,0.7333333333333333,0.6313725490196078,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"nm":"Relleno 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-31,-31],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transformar"}],"nm":"Elipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":0,"nm":"Precomp. 1","tt":1,"refId":"comp_0","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[200,200,0],"ix":2},"a":{"a":0,"k":[200,200,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"w":400,"h":400,"ip":0,"op":144,"st":0,"bm":0}]}
  • 封装为全局指令

image.png

directives/myLoading/index.js

import Vue from 'vue'
import Loading from './MyLoading.vue'
/**
 * Vue.extend 接受参数并返回一个构造器,new 该构造器可以返回一个组件实例
 * 当我们 new Mask() 的时候,把该组件实例挂载到一个 div 上
 **/
const Mask = Vue.extend(Loading)
//   myLoadingType: 'default'

// 更新是否显示
const toggleLoading = (el, binding) => {
  if (binding.value) {
    Vue.nextTick(() => {
      // 控制loading组件显示
      el.instance.visible = true
      el.style.position = 'relative'
      // 插入到目标元素
      insertDom(el, el)
    })
  } else {
    el.instance.visible = false
    el.style.position = 'static'
    el.mask && el.mask.parentNode && el.mask.parentNode.removeChild(el.mask)
  }
}

// 插入到目标元素
const insertDom = (parent, el) => {
  parent.appendChild(el.mask)
}

export default {
  // 第一次绑定到元素时调用
  bind: function (el, binding, vnode) {
    const { modifiers } = binding
    let myLoadingType = 'default'
    if (modifiers) {
      const arr = Object.keys(modifiers)
      if (arr.length) {
        console.log('获取自定义指令myLoading修饰符 ======>', modifiers)
        myLoadingType = ['lottie', 'lottie1', 'lottie2', 'lottie3', 'lottie4'].includes(arr[0]) ? arr[0] : 'default'
      }
    }
    const mask = new Mask({
      el: document.createElement('div'),
      data () {
        return {
          myLoadingType: myLoadingType
        }
      }

    })
    // console.log('mask====>', mask)
    // 用一个变量接住mask实例
    el.instance = mask
    el.mask = mask.$el
    el.maskStyle = {}
    binding.value && toggleLoading(el, binding)
  },
  // 所在组件的 VNode 更新时调用--比较更新前后的值
  update: function (el, binding) {
    if (binding.oldValue !== binding.value) {
      toggleLoading(el, binding)
    }
  },
  // 指令与元素解绑时调用
  unbind: function (el, binding) {
    el.instance && el.instance.$destroy()
  }
}

directives/myLoading/MyLoading.vue

<template>
  <div class="myLoading-container borderRed">
    <!-- 默认加载loading -->
    <div v-if="myLoadingType === 'default'" class="loader">
      <div class="my-loading" />
      <div class="my-loading-text">加载中...</div>
    </div>

    <!-- <MyLoading v-if="myLoadingType === 'default'" /> -->

    <!-- MyLottie 动画loading -->
    <MyLottie v-else-if="myLoadingType === 'lottie'" />
    <MyLottie v-else-if="myLoadingType === 'lottie0'" lottie-type="loading0" />
    <MyLottie v-else-if="myLoadingType === 'lottie1'" lottie-type="loading1" />
    <MyLottie v-else-if="myLoadingType === 'lottie2'" lottie-type="loading2" />
    <MyLottie v-else-if="myLoadingType === 'lottie3'" lottie-type="loading3" />
    <MyLottie v-else-if="myLoadingType === 'lottie4'" lottie-type="loading4" />
  </div>
</template>

<script>
export default {
  name: 'MyLoading'
}
</script>

<style lang ="scss"  scoped>
.myLoading-container {
  height: 100%;
  width: 100%;
  position: absolute;
  top: 0;
  left: 0;
  min-height: 60px;
  background-color: rgba(255, 255, 255, 0.8);
}

/* 默认加载动画 */
.loader {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 60px;
}

.loader .my-loading {
  position: relative;
  width: 100%;
  height: 10px;
  border: 1px solid $color-primary;
  border-radius: 10px;
  animation: turn 1s linear 0.42s infinite;
}

.loader .my-loading:before {
  content: "";
  display: block;
  position: absolute;
  width: 0%;
  height: 100%;
  background: $color-primary;
  box-shadow: 10px 0px 15px 0px $color-primary;
  animation: load .5s linear infinite;
}

.loader .my-loading-text {
  width: 100%;
  position: absolute;
  top: 10px;
  color: $color-primary;
  text-align: center;
  animation: bounce .5s linear infinite;
}

@keyframes load {
  0% {
    width: 0%;
  }

  87.5%,
  100% {
    width: 100%;
  }
}

@keyframes turn {
  0% {
    transform: rotateY(0deg);
  }

  6.25%,
  50% {
    transform: rotateY(180deg);
  }

  56.25%,
  100% {
    transform: rotateY(360deg);
  }
}

@keyframes bounce {
  0%,
  100% {
    top: 10px;
  }

  12.5% {
    top: 30px;
  }
}
/* 默认加载动画 */

</style>

  • 注册指令
import myLoading from './myLoading'

export default {
  install (Vue) {
    // 自定义loading指令
    Vue.directive('myLoading', myLoading)
  }
}

  • 页面使用
      <div slot="content" flex>
        <div
          v-myLoading="true"
          class="dk-container-h350w350 dk-mr-10 borderRed"
          flex="main:center cross:center"
        >
          测试
        </div>

        <div
          v-myLoading.lottie="true"
          class="dk-container-h350w350 dk-mr-10 borderRed"
          flex="main:center cross:center"
        >
          测试
        </div>

        <div
          v-myLoading.lottie1="true"
          class="dk-container-h350w350 dk-mr-10 borderRed"
          flex="main:center cross:center"
        >
          测试
        </div>

        <div
          v-myLoading.lottie2="true"
          class="dk-container-h350w350 dk-mr-10 borderRed"
          flex="main:center cross:center"
        >
          测试
        </div>

        <div
          v-myLoading.lottie3="true"
          class="dk-container-h350w350 dk-mr-10 borderRed"
          flex="main:center cross:center"
        >
          测试
        </div>
      </div>

【补充】:如果有什么看不懂的,可以将上方的源码下载下来,copy代码

完结散花...