框架:vue

<template>
	<!-- viewBox属性是用于指定用户SVG图像的坐标系统的原点以及尺寸的
	不管svg像素多少,都是一个0,0为原点,宽高200个单位的坐标系统,单位与像素无关
	宽高100个单位,即坐标系宽高的一半,border是坐标系单位,不是像素单位
	 -->
  <svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg"
    :width="width+'rem'" :height="width+'rem'"
  >
    <circle cx="100" cy="100" :r="100 - border / 2" 
      fill="none"
      opacity="1"
      stroke="#fff" 
      :stroke-width="border"
    />
    <circle cx="100" cy="100" :r="100 - border / 2" 
      fill="none"
      transform-origin="50% 50%"
      transform="rotate(90)" 
      :stroke="color" 
      :stroke-width="border"
      :stroke-dasharray="dashLen" 
      :stroke-dashoffset = "dashOffset"
      stroke-linecap="round"
      style="transition: stroke-dashoffset 0.4s"
    />
    <text x="100" y="100" text-anchor="middle"
      fill="#ff6321"
      :font-size="45"
      font-weight="600" >
      {{timeLeft}}{{text}}
    </text>
    <text x="100" y="140" text-anchor="middle"
      :font-size="30" >
      {{tips}}
    </text>
  </svg>
</template>

<script>
export default {
  name: 'circlePercent',
  props: {
    target: {
      type: Number,
      required: true
    },
    count: {
      type: Number,
      required: true
    },
    width: {
      type: Number,
      default: 1
    },
    border: {
      type: Number,
      default: 28
    },
    color: {
      type: String,
      default: '#fcaa55'
    },
    fontSize: {
      type: Number,
      default: 30
    }, 
    delay: {
      type: Number,
      default: 2
    }, 
    tips:{
		type:String, 
		default:'完成'
    },
    text:{
    	type:String, 
    	default:'%'
    }
  },
  data () {
    return {
      timeLeft: 0,
      dashLen: (100 - this.border / 2) * Math.PI * 2
    }
  },
  computed: {
    dashOffset () {
      return this.dashLen - this.timeLeft / this.count * this.dashLen
    }
  },
  mounted () {
    var _this = this
  	setTimeout(function(){
  		_this.run()
  	}, _this.delay)
  }, 
  methods:{
  	run:function() {
	    this.timeLeft = 0

      var _this = this
	    this.interval = setInterval(function (){

	      var diff = _this.target / 10
	      _this.timeLeft += diff < 1 ? 1 : diff

	      _this.timeLeft = parseInt(_this.timeLeft)
	      
	      if (_this.timeLeft >= _this.target) {
	        _this.timeLeft = _this.target
	        clearInterval(_this.interval)
	      }

	    }, 60)
  	}
  }
}
  
</script>

大致原理:

svg相当于一块画布,可以设置宽高,这里使用

:width="width+'rem'" :height="width+'rem'"

主要为移动设备提供,可以自行修改为px

svg中的viewbox相当于自定义单位,也就是自己的坐标轴,这里viewbox没有px或者rem单位,只是简单的坐标单元格单位,这里设置0,0,200,200表示左上角坐标是0,0,宽高各200个单位,也就是右下角坐标为200,200

进度条部分:

两个circle,一个做进度条的背景色,一个做进度占比的背景色,主要用stroke做环和环上的进度条

stroke表示线段的颜色,也就是那个环

fill表示圆的背景色,stroke-width表示线条的宽度,也就是那个环的厚度

stroke-dasharray和stroke-dashoffset表示画一条虚线,stroke-dasharray表示虚线中的一段线+虚线中的一段间隔长度,设置为进度占比的颜色,stroke-dashoffset表示虚线的间隔的长度,那么dasharry-dashoffset就是虚线中的线段的长度,设计好比例就能实现环形的比例

圆的起始位置不一定是自己想要的,我想从6点钟开始转,于是设置transform:rotate,参数自己设置,

stroke-linecap="round"是进度比的虚线有弧度,不过设置了dasharray和dashoffset就只能看到一段了,看起来像是那唯一的一条线。

style="transition: stroke-dashoffset 0.4s"本意是设置动画,但是不一定生效,iPhone上开启减弱动态效果会使部分动画效果失效,其他手机看情况,所以我自己实现了run函数,用来控制进度

transform rotate是用来旋转带颜色的环的,默认是左上角的顶点开始旋转,默认是x的正方向轴为0度,而我需要进度条从顶点开始运动,所以需要旋转,

transform-origini是设置旋转的中心的,不想从左上角的顶点开始旋转,想从中心旋转就设置transform-origin: "50% 50%"

transform scale可以用来左右翻转scale(-1, 1),或者垂直翻转scale(1, -1),这个跟环的前进方向有关,有时候需要逆时针,有时候需要顺时针

 

 

两个text,是在环形进度条中间部分写点东西,也可以自定义

至于函数中的var _this = this,主要是兼容es5,可能有的浏览器不兼容es6语法()=>{}

run函数是控制进度的,每隔60ms更新当前进度达到进度条从0开始跑动的效果

使用:

main.js中注册

Vue.component(circlePercent.name, circlePercent)

其他vue文件中调用

<circlePercent 
          :count="100" 
          :target="student.score" 
          :delay="800" 
          class="percent1">        
        </circlePercent>

主要使用了svg画进度条,其他框架或纯html、js可以通用

android 环形进度 前端环形进度条_宽高

这个环设置了transform: "rotate(90deg) scale(-1, 1)" transform-origin:"50% 50%"

感兴趣的可以自定义