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>

猜你喜欢

转载自blog.csdn.net/JackieDYH/article/details/125603617