donut chart

by Jose Quichiz

HTML

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<template id="ppchart">
  <div class="donut-chart">
    <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
       width="400" height="250" viewBox="0 0 400 200" enable-background="new 0 0 400 250" xml:space="preserve">

      <!--defs>
          <linearGradient id="quotaDonut__gradient">
            <stop :stop-color="finalColor" offset="0%"></stop>
            <stop :stop-color="initialColor" class="quotaDonut__gradientStopEnd" offset="100%"></stop>
          </linearGradient>
        </defs-->

      <g transform="translate(200, 200)">
        <circle :r="svgValues.backCircle.radius" class="circle-back" :style="{strokeDasharray: svgValues.backCircle.strokeDasharray + 'px',strokeDashoffset: svgValues.backCircle.strokeDashoffset + 'px', strokeWidth: thickness + 'px'}" />
        <circle :r="svgValues.topBorderCircle.radius" class="circle-border top" :style="{strokeDasharray: svgValues.topBorderCircle.strokeDasharray + 'px',strokeDashoffset: svgValues.topBorderCircle.strokeDashoffset + 'px'}" />
        <circle :r="svgValues.bottomBorderCircle.radius"  class="circle-border bottom" :style="{strokeDasharray: svgValues.bottomBorderCircle.strokeDasharray + 'px',strokeDashoffset: svgValues.bottomBorderCircle.strokeDashoffset + 'px'}"/>
        <circle :r="svgValues.frontCircle.radius" class="circle-front" :style="{strokeDasharray: svgValues.frontCircle.strokeDasharray + 'px',strokeDashoffset: svgValues.frontCircle.strokeDashoffset + 'px', strokeWidth: thickness + 'px', stroke: strokeColor}" />
        <text x="-160" y="25" font-family="monospace" font-size="20px" text-anchor="middle" fill="black">{{minValue}}</text>
        <text x="160" y="25" font-family="monospace" font-size="20px" text-anchor="middle" fill="black">{{maxValue}}</text>
        <text x="0" y="-50" font-family="monospace" font-size="25px" text-anchor="middle"...

CSS

.donut {
  margin: 20px;
}


circle {
  fill: none;
  transform: rotate(180deg);
}

.circle-back {
  stroke: #E0E0E0;
}

.circle-border {
  stroke: #e5e5e5;
  stroke-width: 1px;
}


.circle-front {
  /* stroke: url(#quotaDonut__gradient); */
  /* 2 * Math.PI * radius + 1  */
/*  stroke-dashoffset: 620px;*/
  
  transition: stroke-dashoffset .5s ease, stroke .5s;
}

JavaScript

Vue.component('donnut-chart', {
	props: {
  	currentValue: {
    	type: Number,
      default: 100
    },
    initialColor: {
    	type: String,
      default: '#f50'
    },
    finalColor: {
    	type: String,
      default: '#fc0023'
    },
    strokeColorByPercentage: {
    	type: Array,
      default: function(){
      	return [
          [33.3, "#8BC34A"],
					[66.6, "#FFC107"],
          [100, "#FF5722"]
        ]
      }
    },
    minValue: {
    	type: Number,
      default: 0
    },
    maxValue: {
    	type: Number,
      default: 100
    },
    thickness: {
	    type: Number,
      default: 35
    }
  },
  computed: {
  	percentage: function(){
    	const curr = this.currentValue - this.minValue
    	const top = this.maxValue - this.minValue
    	
    	return (curr * 100) / top
    },
    strokeColor: function(){
      for(var i = 0; i < this.strokeColorByPercentage.length; i++){
        if(this.currentValue <= this.strokeColorByPercentage[i][0]){
        	return this.strokeColorByPercentage[i][1]
        }
      }
      return "black"
    },
    svgValues: function(){
    	const r = 200
			const borderOffset = this.thickness / 2
      const strokeCircle = 2 * Math.PI * (r - borderOffset)
      const strokeOutCircle = 2 * Math.PI * r
      const strokeInCircle = 2 * Math.PI * (r - borderOffset * 2)
      
    	return {
      	backCircle: {
        	radius: r - borderOffset,
          strokeDasharray: strokeCircle,
          strokeDashoffset: strokeCircle/2,
        },
      	frontCircle: {
        	radius: r - borderOffset,
          strokeDasharray: strokeCircle,
          strokeDashoffset: strokeCircle * (1 - this.percentage / 200),
        },
      	topBorderCircle: {
        	radius: r,
          strokeDasharray: strokeOutCircle,
          strokeDashoffset: strokeOutCircle/2,
        },
      	bottomBorderCircle: {
        	radius: r - borderOffset * 2,
          strokeDasharray: strokeInCircle,
          strokeDashoffset: strokeInCircle/2,
     ...