一、问题
由于需要兼容低版本ie,使用的echarts版本较低,为2.2.x。当前版本有饼图百分比不为100%的问题,如数据为如下:
[
{ name: ‘测试1’, value: 1 },
{ name: ‘测试2’, value: 1 },
{ name: ‘测试3’, value: 1 }
]
当前版本饼图计算的百分比均为33.33%,最新版本计算的百分比是33.34%、33.33%、33.33%。
二、解决方法
搜索了一些文章是说用了最大余数法,但是在外部使用插件时无法使用这个算法,又不能改数据。只能修改源码。
1.增加转为百分数方法
(是从最新版本的number.ts拷贝过来修改ts格式为js,并处理为UglifyJs支持的方法)
文件路径echarts/util/number.js
顶部引入zrUtil:
var zrUtil = require('zrenderjs/tool/util');
增加的方法:
/**
* 获取给定精度的数据,确保valueList中的百分比之和为1
* 采用最大余数法
* @param {number[]} valueList 所有数据的列表
* @param {number} idx 数据的索引
* @param {number} precision 精度
* @return {number} 百分数
*/
function getPercentWithPrecision(valueList, idx, precision) {
if (!valueList[idx]) {
return 0;
}
var sum = valueList.reduce(function (acc, val) {
return acc + (isNaN(val) ? 0 : val);
}, 0);
if (sum === 0) {
return 0;
}
var digits = Math.pow(10, precision);
var votesPerQuota = zrUtil.map(valueList, function (val) {
return (isNaN(val) ? 0 : val) / sum * digits * 100;
});
var targetSeats = digits * 100;
var seats = zrUtil.map(votesPerQuota, function (votes) {
// Assign automatic seats.
return Math.floor(votes);
});
var currentSum = seats.reduce(function (acc, val) {
return acc + val;
}, 0);
var remainder = zrUtil.map(votesPerQuota, function (votes, idx) {
return votes - seats[idx];
});
// Has remainding votes.
while (currentSum < targetSeats) {
// Find next largest remainder.
var max = -Infinity;//Number.NEGATIVE_INFINITY;
var maxId = null;
for (var i = 0, len = remainder.length; i < len; ++i) {
if (remainder[i] > max) {
max = remainder[i];
maxId = i;
}
}
// Add a vote to max remainder.
++seats[maxId];
remainder[maxId] = 0;
++currentSum;
}
return seats[idx] / digits;
}
最后增加抛出的方法
return {
//...
getPercentWithPrecision: getPercentWithPrecision
}
2.修改百分数计算值
文件路径:echarts/chart/pie.js
顶部引入转百分比方法
var getPercentWithPrecision = require('echarts/util/number').getPercentWithPrecision;
绘图时计算百分比修改
/**
* 构建单个饼图
*
* @param {number} seriesIndex 系列索引
*/
_buildSinglePie: function (seriesIndex) {
//...
var percentPrecision = (serie.percentPrecision || serie.percentPrecision === 0) ? serie.percentPrecision : 2;//百分数精度(增加percentPrecision配置)
//...
for (var i = 0, l = data.length; i < l; i++) {
//...
var valueList = zrUtil.map(data, function(obj) {
return obj.value
});
percent = getPercentWithPrecision(valueList, i, percentPrecision) / 100;//修改百分比赋值,调用新方法
//...
percent = (percent * 100).toFixed(percentPrecision);//百分数精度从配置取
}
}
三、算法原理
根据小数精度,将每一项占总和的百分数扩大至整数,比如[100,100,100],第一项计算为3333.33333333,然后整数部分就是3333,将余数求出来就是0.33333333,然后3项做同样的操作,得到余数数组。
判断百分数整数之和是否为100*小数精度
,如果小于100*小数精度
说明最后的百分比之和会小于100%,否则没有问题直接返回即可。
若百分比之和小于100%:通过遍历余数数组找到最大的余数,说明这一项占比比其他2项更大,因此给这一项的百分数整数+1,就得到3334,最后的结果整数为[3334,3333,3333],然后将百分数整数转为百分数即[33.34,33.33,33.33]。
四、遇到的报错
from UglifyJs
Unexpected token: name (currentSum)
原因:map要用zrUtil中的map方法,因为map是es6的内容,UglifyJs不支持。
reduce可以直接用,es3就支持了。
from UglifyJs
Unexpected token: operator (>)
原因:使用了箭头函数,修改箭头函数为function匿名函数。