<div id="app">
  <div class="loading-demo">
    <loading :dot-color-val="dotColor" :dot-nums="dotNums" :loading-radius-val="loadingRadius" :dot-radius-val="dotRadius" :style="changeStyle"></loading>
  </div>
  
  <div class="control">
    <div class="control-form">
      <label for="dotNums">圆点个数:</label>
      <input type="number" step="1" min="10" max="20" id="dotNums" v-model.number="dotNums"   />
    </div>
    <div class="control-form">
      <label for="loadingRadius">容器半径:</label>
      <input type="range" min="50" max="200" step="1" id="loadingRadius" v-model.number="loadingRadius"  />
    </div>
    <div class="control-form">
      <label for="dotRadius">圆点半径:</label>
      <input type="range" min="8" max="30" step="1" id="dotRadius" v-model.number="dotRadius"  />
    </div>
    <div class="control-form">
      <label for="dotColor">圆点颜色:</label>
      <input type="color" v-model="dotColor" id="dotColor"  />
    </div>
  </div>
</div>

<template id="loading">
  <div class="loading">
    <div v-for="(dotNum, index) in dotNums" :key="index" :style="dotTransform(index, dotNums)">
      <span :style="dotAimation(index, dotNums)"></span>
    </div>
  </div>
</template>
// >>>>>>>>>>> Let's Go!!!! >>>>>>>>>>>>>>>

body {
  width: 100vw;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}
#app {
  width: 100vw;
  height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  
  > div {
    margin: 0 2rem;
  }
}

:root {
  --loadingRadius: 168px;
  --dotRadius: 24px;
  --dotColor: #d8d8d8;
}

.loading{
  position: relative;
  font-size: 0;
  border-radius: 50%;
  transform-origin: center;
  
  width: var(--loadingRadius);
  height: var(--loadingRadius);
  color: var(--dotColor);
  
  div {
    position: absolute;
    top: 50%;
    left: 50%;
    border-radius: 100%;

    width: var(--dotRadius);
    height: var(--dotRadius);
    color: var(--dotColor);
    margin-top: calc((var(--dotRadius) / 2) * -1);
    margin-left: calc((var(--dotRadius) / 2) * -1);
    
  }
  
  span {
    display: block;
    background-color: currentColor;
    border: 0 solid currentColor;
    border-radius: 100%;
    animation: ball-spin 1s infinite ease-in-out;  
    
    width: var(--dotRadius);
    height: var(--dotRadius);
  }
}

// animation keyframes

@keyframes ball-spin {
    0%,
    100% {
      opacity: 1;
      transform: scale(1);
    }
    20% {
      opacity: 1;
    }
    80% {
      opacity: 0;
      transform: scale(0);
    }
}

.control-form {
  margin: 10px 0;
}
.loading-demo {
  min-width: 200px;
}
View Compiled
Vue.component('loading',{
  template: '#loading',
  props: {
    loadingRadiusVal: {
      type: Number,
      required: true,
      default: 168
    },
    dotRadiusVal: {
      type: Number,
      required: true,
      default: 24
    },
    dotColorVal: {
      type: String,
      required: true,
      default: '#d8d8d8'
    },
    dotNums: {
      type: Number,
      required: true,
      default: 10
    }
  },
  methods: {
    dotTransform: function(index, dotNums) {
      let rad = 2 * Math.PI / dotNums  * index;
      let dotX =  Math.cos(rad) * this.loadingRadiusVal / 2;
      let dotY =  Math.sin(rad) * this.loadingRadiusVal / 2;
      return {
        transform: `translate(${dotX}px,${dotY}px)`
      };
    },
    dotAimation: function(index, dotNums) {
      let delayTime = `${-1 * (1 + (index + 1) * 1 / dotNums) }s`
      return {
        animationDelay: delayTime
      }
    }
  }
  
})

let app = new Vue({
  el: '#app',
  data () {
    return {
      loadingRadius: 168,
      dotRadius: 20,
      dotColor: '#ff3366',
      dotNums: 12
    }
  },
  computed: {
    changeStyle: function() {
      let rootEle = document.documentElement;
      console.log(rootEle)
      rootEle.style.setProperty('--loadingRadius', `${this.loadingRadius}px`)
      rootEle.style.setProperty('--dotRadius', `${this.dotRadius}px`)
      rootEle.style.setProperty('--dotColor', this.dotColor)
    }
  }
})
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js