191 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			191 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | |
|   <div class="DoughnutChart-wrapper">
 | |
|     <div class="DoughnutChart" :id="id">
 | |
|       <canvas :id="canvasId"></canvas>
 | |
|       <div class="DonutChart-text">
 | |
|         <span>{{ ratio }}%</span>
 | |
|       </div>
 | |
|     </div>
 | |
|     <div class="DoughnutChart-explain">
 | |
|       <div class="item" v-for="(item, index) in value" :key="index">
 | |
|         <i></i>
 | |
|         <span :style="labelStyle">{{ item.key }}</span>
 | |
|         <p>{{ item.value }}</p>
 | |
|       </div>
 | |
|     </div>
 | |
|   </div>
 | |
| </template>
 | |
| 
 | |
| <script>
 | |
|   export default {
 | |
|     props: ['ratio', 'value', 'labelStyle'],
 | |
| 
 | |
|     data () {
 | |
|       return {
 | |
|         id: `DonutChart-${Math.ceil(Math.random() * 10000)}`,
 | |
|         canvasId: `DonutChartCanvas-${Math.ceil(Math.random() * 10000)}`,
 | |
|         canvasWidth: 90,
 | |
|         canvasHeight: 90
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     watch: {
 | |
|       ratio () {
 | |
|         this.init()
 | |
|       }
 | |
|     },
 | |
| 
 | |
|     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 = 4
 | |
|         ctx.strokeStyle = 'rgba(102, 121, 138, 0.3)'
 | |
|         ctx.beginPath();
 | |
|         ctx.arc(x, y, x - 8, 0, 2 * Math.PI)
 | |
|         ctx.stroke()
 | |
| 
 | |
|         ctx.beginPath()
 | |
|         ctx.lineWidth = 4
 | |
|         var g = ctx.createLinearGradient(0, 0, 0, 80)
 | |
|         g.addColorStop(0, 'rgba(44, 150, 231, 0.8)')
 | |
|         g.addColorStop(1, 'rgba(92, 255, 243, 1)')
 | |
|         ctx.strokeStyle = g
 | |
| 
 | |
|         if (this.ratio < 25) {
 | |
|           radian = 3 / 2 + angle
 | |
|           ctx.arc(x, y, x - 8, Math.PI + Math.PI / 2, Math.PI * radian, false)
 | |
|         } else if (this.ratio === 100) {
 | |
|           ctx.arc(x, y, x - 8, 0, Math.PI * 2)
 | |
|         } else {
 | |
|           radian = (this.ratio - 25) / 100 * 2
 | |
|           ctx.arc(x, y, x - 8, Math.PI + Math.PI / 2, Math.PI * radian, false)
 | |
|         }
 | |
|         ctx.stroke()
 | |
| 
 | |
|         ctx.beginPath()
 | |
|         ctx.strokeStyle = 'rgba(102, 121, 138, 0.4)'
 | |
|         ctx.lineWidth = 1
 | |
|         ctx.arc(x, y, x - 15, 0, 2 * Math.PI)
 | |
|         ctx.stroke()
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| </script>
 | |
| 
 | |
| <style lang="scss" scoped>
 | |
|   .DoughnutChart-wrapper {
 | |
|     display: flex;
 | |
|     align-items: center;
 | |
|     justify-content: space-between;
 | |
|     height: 100%;
 | |
|     padding-top: 12px;
 | |
| 
 | |
|     .DoughnutChart-explain {
 | |
|       flex: 1;
 | |
|       margin-left: 10px;
 | |
| 
 | |
|       .item {
 | |
|         display: flex;
 | |
|         align-items: center;
 | |
| 
 | |
|         &:first-child {
 | |
|           margin-bottom: 10px;
 | |
|         }
 | |
| 
 | |
|         i {
 | |
|           width: 8px;
 | |
|           height: 8px;
 | |
|           margin-right: 6px;
 | |
|           border-radius: 50%;
 | |
|           background: #5AF9F0;
 | |
|         }
 | |
| 
 | |
|         span {
 | |
|           width: 78px;
 | |
|           font-weight: 400;
 | |
|           font-size: 14px;
 | |
|           color: #9BB7D4;
 | |
|         }
 | |
| 
 | |
|         p {
 | |
|           font-size: 14px;
 | |
|           color: #FFFFFF;
 | |
|         }
 | |
| 
 | |
|         &:last-child i {
 | |
|           background: rgba(102, 121, 138, 0.3);
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
| 
 | |
|     .DoughnutChart {
 | |
|       position: relative;
 | |
|       width: 90px;
 | |
|       height: 90px;
 | |
|       overflow: hidden;
 | |
| 
 | |
|       .DonutChart-text {
 | |
|         display: flex;
 | |
|         position: absolute;
 | |
|         justify-content: center;
 | |
|         top: 50%;
 | |
|         left: 50%;
 | |
|         z-index: 1;
 | |
|         transform: translate(-50%, -50%);
 | |
| 
 | |
|         span {
 | |
|           font-size: 17px;
 | |
|           font-weight: bold;
 | |
|           color: #02FEFF;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| </style>
 |