Vue使用G2Plot-Pie饼图实现数据渲染-和类似0.1+0.2导致的精度问题
前言
笔者,在使用Pie饼图的时候,有时候需要调整显示内容,比如在饼图中间显示总计金额,
但是,在使用的时候,会发现一个经典的问题,JavaScript计算类似0.1+0.2的时候,会出现精度失真的情况,那么这个时候就需要自己二次处理一下计算过程,已解决失真的问题
图示
首先定义组件
<!--
* @Author: Jackie
* @Date: 2022-06-16 15:09:39
* @LastEditTime: 2022-07-04 16:48:11
* @LastEditors: Jackie
* @Description: 饼图
* @FilePath: /white-label-nft-admin-client/src/components/Charts/PieChart.vue
* @version:
-->
<template>
<div id="PieChart" ref="PieChart"></div>
</template>
<script>
import { Pie } from '@antv/g2plot'
let chartChange
export default {
name: 'PieChart',
props: {
value: {
type: Array,
default() {
return []
},
},
Height: {
type: Number,
default: 0,
},
},
// 监听
watch: {
value: {
handler(newVal, oldVal) {
console.log(newVal)
// 更新数据
chartChange.changeData(newVal)
// 销毁后再渲染
// chartChange.destroy()
// this.initG2Plot()
},
deep: true, //深度监听
// immediate: true,
},
},
mounted() {
this.initG2Plot()
},
methods: {
initG2Plot() {
chartChange = new Pie(this.$refs.PieChart, {
data: this.value,
height: this.Height,
appendPadding: 10,
angleField: 'value',
colorField: 'type',
radius: 0.9,
innerRadius: 0.64,
meta: {
value: {
formatter: (v) => `¥ ${v}`,
},
},
legend: {
ayout: 'vertical',
position: 'right',
flipPage: true,
offsetX: -20,
// title: {
// text: '产品类别 (销售量占比)',
// spacing: 8,
// },
// itemValue: {
// formatter: (text, item) => {
// const items = this.value.filter((d) => d.type === item.value)
// return items.length ? '¥' + items.reduce((a, b) => a + b.value, 0) / items.length : '-'
// },
// style: {
// opacity: 0.65,
// },
// },
},
label: {
type: 'outer',
content: '{name} - {value}元 | 占比{percentage}',
// type: 'inner',
// offset: '-50%',
// autoRotate: false,
// style: { textAlign: 'center' },
// formatter: ({ percent }) => `${(percent * 100).toFixed(0)}%`,
style: {
fill: '#000',
},
},
// 圆心配置
statistic: {
title: {
offsetY: -10,
content: '总计',
},
content: {
offsetY: 10,
},
},
// 添加 中心统计文本 交互
interactions: [
{ type: 'pie-legend-active' },
{ type: 'element-active' },
// { type: 'element-selected' },
// {
// type: 'pie-statistic-active',
// cfg: {
// start: [
// { trigger: 'element:mouseenter', action: 'pie-statistic:change' },
// { trigger: 'legend-item:mouseenter', action: 'pie-statistic:change' },
// ],
// end: [
// { trigger: 'element:mouseleave', action: 'pie-statistic:reset' },
// { trigger: 'legend-item:mouseleave', action: 'pie-statistic:reset' },
// ],
// },
// },
],
})
chartChange.render()
},
},
}
</script>
使用的时候当传入数据有小数时,容易出现问题
[ { type: '活动01', value: 20 }, { type: '活动02', value: 30 }, { type: '活动03', value: 30 }, { type: '活动04', value: 50 }, { type: '活动05', value: 20 }, { type: '活动06', value: 80.14 }, { type: '活动07', value: 0.01 }, ]
解决问题
当出现这个问题时,我就明白是JavaScript计算精度导致的,那么只能自己处理这个累计的值,来解决这个问题
查阅官方文档
参照文档
解决方法:失真是由于小数相加导致的,那么我们在相加之前,先扩大100倍,得到结果后缩小100倍,这样正确的值就得到了
// 圆心配置
statistic: {
title: {
offsetY: -10,
content: '总计',
},
content: {
offsetY: 10,
// 解决精度失真问题
// 方法一:value * 1000000000000
// 方法二:parseFloat((0.1+0.2).toFixed(5))
formatter: (value, datum, index, data) => {
const total = datum.reduce((a, b) => {
return a + b.value
// return a + b.value * 1000000000000
}, 0)
return `¥${parseFloat(total.toFixed(5))}`
// return `¥${total / 1000000000000}`
},
},
},
图示
全量代码
<!--
* @Author: Jackie
* @Date: 2022-06-16 15:09:39
* @LastEditTime: 2022-07-04 17:00:37
* @LastEditors: Jackie
* @Description: 饼图
* @FilePath: src/components/Charts/PieChart.vue
* @version:
-->
<template>
<div id="PieChart" ref="PieChart"></div>
</template>
<script>
import { Pie } from '@antv/g2plot'
let chartChange
export default {
name: 'PieChart',
props: {
value: {
type: Array,
default() {
return []
},
},
Height: {
type: Number,
default: 0,
},
},
// 监听
watch: {
value: {
handler(newVal, oldVal) {
console.log(newVal)
// 更新数据
chartChange.changeData(newVal)
// 销毁后再渲染
// chartChange.destroy()
// this.initG2Plot()
},
deep: true, //深度监听
// immediate: true,
},
},
mounted() {
this.initG2Plot()
},
methods: {
initG2Plot() {
chartChange = new Pie(this.$refs.PieChart, {
data: this.value,
height: this.Height,
appendPadding: 10,
angleField: 'value',
colorField: 'type',
radius: 0.9,
innerRadius: 0.64,
meta: {
value: {
formatter: (v) => `¥ ${v}`,
},
},
legend: {
ayout: 'vertical',
position: 'right',
flipPage: true,
offsetX: -20,
// title: {
// text: '产品类别 (销售量占比)',
// spacing: 8,
// },
// itemValue: {
// formatter: (text, item) => {
// const items = this.value.filter((d) => d.type === item.value)
// return items.length ? '¥' + items.reduce((a, b) => a + b.value, 0) / items.length : '-'
// },
// style: {
// opacity: 0.65,
// },
// },
},
label: {
type: 'outer',
content: '{name} - {value}元 | 占比{percentage}',
// type: 'inner',
// offset: '-50%',
// autoRotate: false,
// style: { textAlign: 'center' },
// formatter: ({ percent }) => `${(percent * 100).toFixed(0)}%`,
style: {
fill: '#000',
},
},
// 圆心配置
statistic: {
title: {
offsetY: -10,
content: '总计',
},
content: {
offsetY: 10,
// 解决精度失真问题
// 方法一:value * 1000000000000
// 方法二:parseFloat((0.1+0.2).toFixed(5))
formatter: (value, datum, index, data) => {
const total = datum.reduce((a, b) => {
return a + b.value
// return a + b.value * 1000000000000
}, 0)
return `¥${parseFloat(total.toFixed(5))}`
// return `¥${total / 1000000000000}`
},
},
},
// 添加 中心统计文本 交互
interactions: [
{ type: 'pie-legend-active' },
{ type: 'element-active' },
// { type: 'element-selected' },
// {
// type: 'pie-statistic-active',
// cfg: {
// start: [
// { trigger: 'element:mouseenter', action: 'pie-statistic:change' },
// { trigger: 'legend-item:mouseenter', action: 'pie-statistic:change' },
// ],
// end: [
// { trigger: 'element:mouseleave', action: 'pie-statistic:reset' },
// { trigger: 'legend-item:mouseleave', action: 'pie-statistic:reset' },
// ],
// },
// },
],
})
chartChange.render()
},
},
}
</script>
本文来自博客园,作者:JackieDYH,转载请注明原文链接:https://www.cnblogs.com/JackieDYH/p/17634082.html