svg ring chart
by imys
HTML
<div id="app">
<Ring :percent="percent" color="#4caf50"></Ring>
<input type="range" v-model.number="percent" min="0" max="100" step="1">
{{percent}}
</div>
SCSS
.ring-svg {
position: relative;
cursor: pointer;
path {
fill: none;
stroke-linecap: round;
transition: stroke-dashoffset 0.2s ease 0s, stroke 0.2s ease
}
}
Vue
const Ring = {
template: `
<div :style="sizeStyle"
class="ring-svg">
<svg viewBox="0 0 100 100">
<path stroke="#ddd"
transform="translate(50, 50)"
:stroke-width="strokeWidth"
:d="pathString"
class="back" />
<path transform="translate(50, 50)"
:stroke="color"
:stroke-width="strokeWidth"
:d="pathString"
:style="pathStyle"
class="front" />
</svg>
</div>
`,
props: {
percent: {
type: Number,
default: 0
},
color: {
type: String,
default: '#fff'
},
size: {
type: Number,
default: 120
},
strokeWidth: {
type: Number,
default: 8
},
},
data() {
return {
startAngle: 216,
endAngle: 144,
}
},
computed: {
sizeStyle() {
return {
width: `${this.size}px`,
height: `${this.size}px`
}
},
radius() {
return 50 - (this.strokeWidth / 2)
},
len() {
return Math.PI * 2 * this.radius * 0.8
},
pathString() {
return this.calcPath(this.endAngle)
},
pathStyle() {
const offset = (100 - this.percent) / 100 * this.len
return {
'stroke-dasharray': `${this.len}px ${this.len}px`,
'stroke-dashoffset': `${offset}px`,
}
},
},
methods: {
position(angle) {
const rad = angle * (Math.PI / 180)
const x = (Math.sin(rad) * this.radius).toFixed(2)
const y = -(Math.cos(rad) * this.radius).toFixed(2)
return [x, y]
},
calcPath(endAngle) {
const { radius, startAngle } = this
const start = this.position(startAngle)
const end = this.position(endAngle)
return `M ${start[0]} ${start[1]} A ${radius} ${radius} 0 1 1 ${end[0]} ${end[1]}`
}
}
}
new Vue({
el: '#app',
components: {
Ring
},
data: {
percent:...