如何修改Amcharts5仪表盘图表使其填充至指定值
问题描述
当前使用Vue3组合式API + Nuxt3实现了带指针的Amcharts5仪表盘图表,现需要:
- 将其改为仅填充至指定值的仪表盘样式(参考Figma设计);
- 解决图表无法占满画布且不响应式的问题。
当前实现代码如下:
<template> <div class="gauge-chart" ref="gaugeRef"></div> </template> <script setup> import * as am5 from "@amcharts/amcharts5"; import * as am5xy from "@amcharts/amcharts5/xy"; import * as am5radar from "@amcharts/amcharts5/radar"; import am5themes_Animated from "@amcharts/amcharts5/themes/Animated"; const props = defineProps({ data: { required: true, type: Object, }, gaugeBands: { required: true, type: Array, }, }); const gaugeRef = ref(null); let root; const axisDataItem = shallowRef(null); onMounted(() => { root = am5.Root.new(gaugeRef.value); root.setThemes([am5themes_Animated.new(root)]); const chart = root.container.children.push( am5radar.RadarChart.new(root, { panX: false, panY: false, height: 200, width: 200, centerX: 29, centerY: 35, startAngle: -240, endAngle: 60, }) ); const axisRenderer = am5radar.AxisRendererCircular.new(root, { radius: am5.percent(100), innerRadius: -20, minGridDistance: 0, }); axisRenderer.grid.template.setAll({ stroke: root.interfaceColors.get("background"), visible: false, strokeOpacity: 1, }); const axis = chart.xAxes.push( am5xy.ValueAxis.new(root, { maxDeviation: 0, min: 0, max: 60, strictMinMax: true, renderer: axisRenderer, }) ); axisDataItem.value = axis.makeDataItem({}); const clockHand = am5radar.ClockHand.new(root, { pinRadius: am5.percent(50), radius: am5.percent(75), bottomWidth: 40, topWidth: 1, }); const bullet = axisDataItem.value.set( "bullet", am5xy.AxisBullet.new(root, { sprite: clockHand, }) ); bullet.set("fill", am5.color(0x000000)); axis.createAxisRange(axisDataItem.value); const label = chart.radarContainer.children.push( am5.Label.new(root, { fill: am5.color(0xffffff), centerX: am5.percent(50), textAlign: "center", centerY: am5.percent(50), fontSize: "2.5em", fontWeight: "bold", }) ); axisDataItem.value.set("value", Math.floor(props.data)); bullet.get("sprite").on("rotation", () => { Math.round(axisDataItem.value.get("value")).toString(); let fill = am5.color(0x000000); const value = axisDataItem.value.get("value"); label.set("text", Math.round(value).toString()); axis.axisRanges.each(function (axisRange) { if (value >= axisRange.get("value") && value <= axisRange.get("endValue")) { fill = axisRange.get("axisFill").get("fill"); } }); clockHand.pin.animate({ key: "fill", to: fill, duration: 500, easing: am5.ease.out(am5.ease.cubic), }); clockHand.hand.animate({ key: "fill", to: fill, duration: 500, easing: am5.ease.out(am5.ease.cubic), }); }); chart.bulletsContainer.set("mask", undefined); am5.array.each(props.gaugeBands, (data) => { const axisRange = axis.createAxisRange(axis.makeDataItem({})); axisRange.setAll({ value: data.lowScore, endValue: data.highScore, }); axisRange.get("axisFill").setAll({ visible: true, fill: am5.color(data.color), fillOpacity: 1, }); }); // Make stuff animate on load chart.appear(1000, 100); }); onBeforeMount(() => { if (root) { root.dispose(); } }); watch( () => props.data, (newValue) => axisDataItem.value.animate({ key: "value", to: Math.floor(newValue), duration: 500, easing: am5.ease.out(am5.ease.cubic), }) ); </script> <style> .gauge-chart { left: 200px; width: 200px; height: 200px; } </style>
解决方案
1. 是否可以实现仅填充至指定值的仪表盘?
完全可以。通过Amcharts5的**轴范围(AxisRange)**功能,我们可以创建一个从最小值到目标值的填充段,替代原有的指针样式。
2. 具体实现方法
步骤1:改造仪表盘为填充式样式
- 移除原有的
ClockHand指针相关代码; - 创建一个动态轴范围,对应从
0到目标值props.data的填充区域; - 保留中心数值标签,同步更新显示当前值。
步骤2:实现响应式占满画布
- 移除图表的固定宽高,改为自适应容器尺寸;
- 设置容器为100%宽高;
- 开启Amcharts5的自动 resize 监听。
修改后的完整代码
<template> <div class="gauge-chart" ref="gaugeRef"></div> </template> <script setup> import * as am5 from "@amcharts/amcharts5"; import * as am5xy from "@amcharts/amcharts5/xy"; import * as am5radar from "@amcharts/amcharts5/radar"; import am5themes_Animated from "@amcharts/amcharts5/themes/Animated"; const props = defineProps({ data: { required: true, type: Number, // 修正类型:props.data应为数值而非Object }, gaugeBands: { required: true, type: Array, }, }); const gaugeRef = ref(null); let root; let fillRange; // 用于存储动态填充的轴范围 const centerLabel = shallowRef(null); onMounted(() => { root = am5.Root.new(gaugeRef.value); root._logo.dispose(); // 可选:移除Amcharts水印 root.setThemes([am5themes_Animated.new(root)]); const chart = root.container.children.push( am5radar.RadarChart.new(root, { panX: false, panY: false, startAngle: -240, endAngle: 60, centerX: am5.percent(50), // 改为百分比居中 centerY: am5.percent(50), }) ); const axisRenderer = am5radar.AxisRendererCircular.new(root, { radius: am5.percent(100), innerRadius: -20, minGridDistance: 0, }); axisRenderer.grid.template.setAll({ visible: false, }); const axis = chart.xAxes.push( am5xy.ValueAxis.new(root, { maxDeviation: 0, min: 0, max: 60, strictMinMax: true, renderer: axisRenderer, }) ); // 添加背景分段(原gaugeBands) am5.array.each(props.gaugeBands, (data) => { const axisRange = axis.createAxisRange(axis.makeDataItem({})); axisRange.setAll({ value: data.lowScore, endValue: data.highScore, }); axisRange.get("axisFill").setAll({ visible: true, fill: am5.color(data.color), fillOpacity: 0.3, // 背景分段设为半透明,突出填充段 }); }); // 创建动态填充段:从0到目标值 fillRange = axis.createAxisRange(axis.makeDataItem({})); fillRange.setAll({ value: 0, endValue: Math.floor(props.data), }); fillRange.get("axisFill").setAll({ visible: true, fill: am5.color(0x000000), // 填充段颜色,可根据需求调整 fillOpacity: 1, }); // 添加中心数值标签 centerLabel.value = chart.radarContainer.children.push( am5.Label.new(root, { fill: am5.color(0xffffff), centerX: am5.percent(50), textAlign: "center", centerY: am5.percent(50), fontSize: "2.5em", fontWeight: "bold", text: Math.floor(props.data).toString(), }) ); // 图表初始动画 chart.appear(1000, 100); }); onBeforeUnmount(() => { if (root) { root.dispose(); } }); // 监听数据变化,更新填充段和标签 watch( () => props.data, (newValue) => { const targetValue = Math.floor(newValue); // 动画更新填充段的结束值 fillRange.animate({ key: "endValue", to: targetValue, duration: 500, easing: am5.ease.out(am5.ease.cubic), }); // 更新中心标签 centerLabel.value.set("text", targetValue.toString()); } ); </script> <style scoped> .gauge-chart { width: 100%; height: 100%; min-height: 200px; /* 可选:设置最小高度防止容器过小 */ } </style>
关键修改说明
填充式仪表盘改造:
- 移除了
ClockHand相关代码,改用axisRange创建动态填充段; - 背景分段设置为半透明,让填充段更突出;
- 修正了
props.data的类型定义(应为数值而非Object)。
- 移除了
响应式设置:
- 移除了图表的固定
width/height,改为百分比居中; - 容器样式改为
width: 100%; height: 100%;,占满父容器; - 图表会自动监听容器尺寸变化并适配。
- 移除了图表的固定
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

