vue 实现扇形图表
使用 SVG 和 Vue 实现扇形图表
在 Vue 中实现扇形图表可以通过 SVG 的 <path> 元素结合计算属性动态生成扇形路径。以下是一个完整的实现示例:

<template>
<div class="pie-chart">
<svg width="400" height="400" viewBox="0 0 400 400">
<path
v-for="(item, index) in sectors"
:key="index"
:d="item.path"
:fill="item.color"
@mouseover="handleHover(index)"
/>
</svg>
<div class="legend">
<div v-for="(item, index) in data" :key="index" class="legend-item">
<span :style="{ backgroundColor: colors[index] }"></span>
{{ item.label }}: {{ item.value }}
</div>
</div>
</div>
</template>
<script>
export default {
props: {
data: {
type: Array,
required: true
},
colors: {
type: Array,
default: () => ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF']
}
},
computed: {
sectors() {
const total = this.data.reduce((sum, item) => sum + item.value, 0);
let cumulativeAngle = 0;
return this.data.map((item, index) => {
const angle = (item.value / total) * 360;
const path = this.describeArc(200, 200, 150, cumulativeAngle, cumulativeAngle + angle);
cumulativeAngle += angle;
return {
path,
color: this.colors[index % this.colors.length]
};
});
}
},
methods: {
polarToCartesian(centerX, centerY, radius, angleInDegrees) {
const angleInRadians = (angleInDegrees - 90) * Math.PI / 180;
return {
x: centerX + (radius * Math.cos(angleInRadians)),
y: centerY + (radius * Math.sin(angleInRadians))
};
},
describeArc(x, y, radius, startAngle, endAngle) {
const start = this.polarToCartesian(x, y, radius, endAngle);
const end = this.polarToCartesian(x, y, radius, startAngle);
const largeArcFlag = endAngle - startAngle <= 180 ? '0' : '1';
return [
'M', x, y,
'L', end.x, end.y,
'A', radius, radius, 0, largeArcFlag, 0, start.x, start.y,
'Z'
].join(' ');
},
handleHover(index) {
this.$emit('sector-hover', this.data[index]);
}
}
};
</script>
<style>
.pie-chart {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.legend {
margin-top: 20px;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.legend-item {
display: flex;
align-items: center;
margin: 0 10px;
}
.legend-item span {
display: inline-block;
width: 12px;
height: 12px;
margin-right: 5px;
border-radius: 2px;
}
</style>
使用第三方库(如 ECharts)实现
对于更复杂的扇形图表需求,可以使用 ECharts 这样的专业图表库:

<template>
<div ref="chart" style="width: 600px; height: 400px;"></div>
</template>
<script>
import * as echarts from 'echarts';
export default {
props: {
data: {
type: Array,
required: true
}
},
mounted() {
this.initChart();
},
methods: {
initChart() {
const chart = echarts.init(this.$refs.chart);
const option = {
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'
},
legend: {
orient: 'vertical',
left: 10,
data: this.data.map(item => item.label)
},
series: [
{
name: '数据',
type: 'pie',
radius: ['50%', '70%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '18',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: this.data.map(item => ({
value: item.value,
name: item.label
}))
}
]
};
chart.setOption(option);
}
},
watch: {
data: {
handler() {
this.initChart();
},
deep: true
}
}
};
</script>
实现动画效果
为扇形图表添加动画效果可以增强用户体验:
<script>
// 在SVG实现的组件中添加动画方法
methods: {
animateChart() {
this.animationProgress = 0;
const duration = 1000; // 动画持续时间(ms)
const steps = 60; // 动画帧数
const stepSize = 1 / steps;
const animate = () => {
this.animationProgress += stepSize;
if (this.animationProgress < 1) {
requestAnimationFrame(animate);
}
};
animate();
}
},
computed: {
sectors() {
// 修改计算逻辑以支持动画
const total = this.data.reduce((sum, item) => sum + item.value, 0);
let cumulativeAngle = 0;
return this.data.map((item, index) => {
const fullAngle = (item.value / total) * 360;
const currentAngle = fullAngle * this.animationProgress;
const path = this.describeArc(200, 200, 150, cumulativeAngle, cumulativeAngle + currentAngle);
cumulativeAngle += fullAngle;
return {
path,
color: this.colors[index % this.colors.length]
};
});
}
},
mounted() {
this.animateChart();
}
</script>
响应式设计
确保图表在不同屏幕尺寸下正常显示:
<script>
// 在ECharts实现的组件中添加
methods: {
handleResize() {
if (this.chart) {
this.chart.resize();
}
}
},
mounted() {
this.initChart();
window.addEventListener('resize', this.handleResize);
},
beforeDestroy() {
window.removeEventListener('resize', this.handleResize);
if (this.chart) {
this.chart.dispose();
}
}
</script>
这些实现方法涵盖了从基础SVG绘制到使用专业库的不同方案,可以根据项目需求选择合适的实现方式。






