vue 实现扇形图表
使用 SVG 和 Vue 实现扇形图表
在 Vue 中实现扇形图表可以通过 SVG 的 path 元素结合计算属性动态生成扇形路径。以下是一个完整的实现方法:
安装依赖(如需要动画效果):
npm install vue-property-decorator d3-shape
核心代码实现:
<template>
<div class="pie-chart">
<svg :width="width" :height="height">
<g :transform="`translate(${width/2}, ${height/2})`">
<path
v-for="(item, index) in sectors"
:key="index"
:d="item.d"
:fill="item.color"
@mouseover="handleHover(index)"
/>
</g>
</svg>
</div>
</template>
<script>
import { arc } from 'd3-shape'
export default {
props: {
data: {
type: Array,
required: true
},
width: {
type: Number,
default: 400
},
height: {
type: Number,
default: 400
},
innerRadius: {
type: Number,
default: 0
}
},
computed: {
sectors() {
const total = this.data.reduce((sum, item) => sum + item.value, 0)
const arcGenerator = arc()
.innerRadius(this.innerRadius)
.outerRadius(Math.min(this.width, this.height) / 2)
let currentAngle = 0
return this.data.map(item => {
const angle = (item.value / total) * 2 * Math.PI
const sector = {
d: arcGenerator({
startAngle: currentAngle,
endAngle: currentAngle + angle
}),
color: item.color || getRandomColor(),
value: item.value
}
currentAngle += angle
return sector
})
}
},
methods: {
handleHover(index) {
this.$emit('sector-hover', this.data[index])
}
}
}
function getRandomColor() {
return `#${Math.floor(Math.random()*16777215).toString(16)}`
}
</script>
使用配置项说明
数据格式要求:
data: [
{ value: 30, color: '#FF6384' },
{ value: 50, color: '#36A2EB' },
{ value: 20, color: '#FFCE56' }
]
主要参数说明:
width/height: SVG 画布尺寸innerRadius: 内半径(0为实心饼图,大于0为环形图)outerRadius: 外半径(默认自动计算为画布短边的一半)
添加动画效果
为扇形添加过渡动画:
<path
v-for="(item, index) in sectors"
:key="index"
:d="item.d"
:fill="item.color"
style="transition: all 0.3s ease"
@mouseover="handleHover(index)"
/>
添加交互标签
显示扇形数值标签:
<template>
<text
v-for="(item, index) in sectors"
:key="'label-' + index"
:transform="`translate(${getLabelPosition(index)})`"
text-anchor="middle"
fill="#fff"
>
{{ item.value }}
</text>
</template>
<script>
methods: {
getLabelPosition(index) {
const angle = this.sectors[index].startAngle + (this.sectors[index].endAngle - this.sectors[index].startAngle)/2
const radius = Math.min(this.width, this.height)/2 * 0.7
return [
Math.sin(angle) * radius,
-Math.cos(angle) * radius
]
}
}
</script>
使用第三方库的替代方案
如需快速实现,可以使用 vue-chartjs:
npm install vue-chartjs chart.js
实现代码:
<template>
<pie-chart :chart-data="chartData" :options="options"/>
</template>
<script>
import { Pie } from 'vue-chartjs'
export default {
extends: Pie,
props: ['data'],
data() {
return {
options: {
responsive: true,
maintainAspectRatio: false
}
}
},
mounted() {
this.renderChart({
labels: this.data.map(item => item.label),
datasets: [{
data: this.data.map(item => item.value),
backgroundColor: this.data.map(item => item.color)
}]
}, this.options)
}
}
</script>
性能优化建议
对于大数据量场景:
- 使用
will-change: transform提升动画性能 - 对静态图表使用
v-once - 考虑使用 Canvas 渲染代替 SVG(如通过
vue-chartjs) - 防抖处理鼠标移动事件







