134 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			134 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
<template>
 | 
						|
  <div class="DonutChart" :id="id">
 | 
						|
    <canvas :id="canvasId"></canvas>
 | 
						|
    <div class="DonutChart-text">
 | 
						|
      <span>{{ ratio || 0 }}%</span>
 | 
						|
      <i>{{ text }}</i>
 | 
						|
    </div>
 | 
						|
  </div>
 | 
						|
</template>
 | 
						|
 | 
						|
<script>
 | 
						|
  export default {
 | 
						|
    props: ['ratio', 'text'],
 | 
						|
 | 
						|
    data () {
 | 
						|
      return {
 | 
						|
        id: `DonutChart-${Math.ceil(Math.random() * 10000)}`,
 | 
						|
        canvasId: `DonutChartCanvas-${Math.ceil(Math.random() * 10000)}`,
 | 
						|
        canvasWidth: 90,
 | 
						|
        canvasHeight: 90
 | 
						|
      }
 | 
						|
    },
 | 
						|
 | 
						|
    mounted () {
 | 
						|
      this.$nextTick(() => {
 | 
						|
        this.init()
 | 
						|
      })
 | 
						|
    },
 | 
						|
 | 
						|
    methods: {
 | 
						|
      drawLine(ctx, options) {
 | 
						|
        const { beginX, beginY, endX, endY, lineColor, lineWidth } = options
 | 
						|
        ctx.lineWidth = lineWidth
 | 
						|
        ctx.strokeStyle = lineColor
 | 
						|
        ctx.beginPath()
 | 
						|
        ctx.moveTo(beginX, beginY)
 | 
						|
        ctx.lineTo(endX, endY)
 | 
						|
        ctx.closePath()
 | 
						|
        ctx.stroke()
 | 
						|
      },
 | 
						|
 | 
						|
      angle (a, i, ox, oy, or) {
 | 
						|
        var hudu = (2 * Math.PI / 360) * a * i
 | 
						|
        var x = ox + Math.sin(hudu) * or
 | 
						|
        var y = oy - Math.cos(hudu) * or
 | 
						|
        return x + '_' + y
 | 
						|
      },
 | 
						|
 | 
						|
      mapColor (value) {
 | 
						|
        if (value < 25) {
 | 
						|
          return '#FFC139'
 | 
						|
        }
 | 
						|
 | 
						|
        if (value < 50) {
 | 
						|
          return '#21E03E'
 | 
						|
        }
 | 
						|
 | 
						|
        return '#05C8FF'
 | 
						|
      },
 | 
						|
 | 
						|
      init () {
 | 
						|
        const ctx = document.querySelector(`#${this.canvasId}`).getContext('2d')
 | 
						|
        const canvasWidth = document.querySelector(`#${this.id}`).offsetWidth
 | 
						|
        const canvasHeight = document.querySelector(`#${this.id}`).offsetHeight
 | 
						|
        const angle = this.ratio / 100 * 2
 | 
						|
        let radian = 0
 | 
						|
 | 
						|
        ctx.width = canvasWidth
 | 
						|
        ctx.height = canvasHeight
 | 
						|
        const x = canvasWidth / 2
 | 
						|
        const y = canvasHeight / 2
 | 
						|
        ctx.lineWidth = 2
 | 
						|
        ctx.strokeStyle = '#383f56'
 | 
						|
        ctx.beginPath();
 | 
						|
        ctx.arc(x, y, x - 3, 0, 2 * Math.PI)
 | 
						|
        ctx.stroke()
 | 
						|
 | 
						|
        ctx.beginPath()
 | 
						|
        ctx.lineWidth = 4
 | 
						|
        ctx.strokeStyle = 'rgba(76, 202, 227, 1)'
 | 
						|
 | 
						|
        if (this.ratio < 25) {
 | 
						|
          radian = 3 / 2 + angle
 | 
						|
          ctx.arc(x, y, x - 4, Math.PI + Math.PI / 2, Math.PI * radian, false)
 | 
						|
        } else if (this.ratio === 100) {
 | 
						|
          ctx.arc(x, y, x - 4, 0, Math.PI * 2)
 | 
						|
        } else {
 | 
						|
          radian = (this.ratio - 25) / 100 * 2
 | 
						|
          ctx.arc(x, y, x - 4, Math.PI + Math.PI / 2, Math.PI * radian, false)
 | 
						|
        }
 | 
						|
        ctx.stroke()
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
</script>
 | 
						|
 | 
						|
<style lang="scss" scoped>
 | 
						|
  .DonutChart {
 | 
						|
    position: relative;
 | 
						|
    width: 84px;
 | 
						|
    height: 84px;
 | 
						|
    overflow: hidden;
 | 
						|
 | 
						|
    .DonutChart-text {
 | 
						|
      display: flex;
 | 
						|
      position: absolute;
 | 
						|
      align-items: center;
 | 
						|
      justify-content: center;
 | 
						|
      flex-direction: column;
 | 
						|
      top: 50%;
 | 
						|
      left: 50%;
 | 
						|
      z-index: 1;
 | 
						|
      width: 100%;
 | 
						|
      height: 100%;
 | 
						|
      line-height: 1;
 | 
						|
      transform: translate(-50%, -50%);
 | 
						|
 | 
						|
      span {
 | 
						|
        margin-bottom: 8px;
 | 
						|
        font-size: 20px;
 | 
						|
        font-weight: bold;
 | 
						|
        color: #fff;
 | 
						|
        font-style: oblique;
 | 
						|
      }
 | 
						|
 | 
						|
      i {
 | 
						|
        font-size: 12px;
 | 
						|
        font-style: normal;
 | 
						|
        color: rgba(42, 183, 209, 1);
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
</style>
 |