将rgb颜色切成多份填充,
配置项如下
/*
* 角度(圆锥)渐变
* 原理: 使用axisLine的color来模拟实现, 平滑的渐变过渡借助axisTick实现.
* 其他方案: axisTick的color设为image, 用角度渐变图片填充.
1.当使用ps生成的png图片时,此方案的问题是需要保持echarts大小不变,
尺寸一旦变化就需要修改图片的分辨率, 和裁剪图片,适应场景有限制
* 2. 为了解除ps的限制,可以使用canvas的create-conical-gradient polyfill
根据echarts尺寸以及仪表盘radius动态生成角度渐变的图片填充在axisTick中
*
* 不均匀刻度
* 原理:通过计算每个splitNumber的单位刻度值实现,公式在81行parseValue方法
*/
// 角度渐变的关键色值,在ps中获取
// 一定要注意ps中角度渐变的起始角度要保持和图形的起始角度(startAngle)一致
const colors = [
[0, 235, 15, 15],
[0.1, 235, 15, 15],
[0.16, 255, 246, 0],
[0.4, 15, 235, 103],
[0.54, 15, 235, 103],
[0.67, 255, 246, 0],
[0.78, 235, 15, 15],
[0.9, 0, 99, 177],
[1, 235, 15, 15],
];
// 将如: 0.04-0.16之间的渐变值补全
const gradientColors = [];
colors.forEach((color, i) => {
if (i === colors.length - 1) return;
let maxDiffValue = 0;
const diffs = color.map((s, j) => {
const diff = s - colors[i + 1][j];
const positiveDiff = Math.abs(diff);
if (Math.abs(maxDiffValue) < positiveDiff) {
maxDiffValue = positiveDiff;
}
return diff;
});
gradientColors.push([color[0], `rgb(${color.slice(1).join(',')})`]);
let start = color[0];
const end = colors[i + 1][0];
const step = (end - start) / maxDiffValue;
for (let k = 0; k < maxDiffValue - 1; k++) {
let rgb = 'rgb($0,$1,$2)';
for (let o = 1; o < color.length; o++) {
let c = color[o];
c = c - diffs[o] / maxDiffValue;
color[o] = c;
rgb = rgb.replace(`$${o - 1}`, c);
}
start = start + step;
gradientColors.push([start, rgb]);
}
});
const lastColor = colors[colors.length - 1];
gradientColors.push([lastColor[0], `rgb(${lastColor.slice(1).join(',')})`]);
// 测试数据
const testData = {
startAngle: 225,
endAngle: -45,
splitNumber: 6,
max: 65,
data: [30],
};
// 仪表盘axisLabel展示的值,个数为splitNumber + 1
const axisLabelData = [0, 40, 45, 50, 55, 60, 65];
// 仪表盘axisLabel根据splitNumber生成的值
const axisLabelOriginData = (() => {
const data = [];
for (let index = 0; index < testData.splitNumber + 1; index++) {
// echarts自动计算axisLabel时, 精确的是10位小数
data.push(+((testData.max / testData.splitNumber) * index).toFixed(10));
}
return data;
})();
// 将正常值转为指针偏转的值
function parseValue(x, [kp, kn], [zp, zn]) {
if (x === 0) return 0;
const y = ((zn - zp) / (kn - kp)) * (x - kp) + zp;
return y;
}
let value = testData.data[0];
let valueIndex = -1;
axisLabelData.some((item, index) => {
valueIndex = index;
return value <= item;
});
const k = [axisLabelData[valueIndex - 1], axisLabelData[valueIndex]];
const z = [axisLabelOriginData[valueIndex - 1], axisLabelOriginData[valueIndex]];
value = parseValue(value, k, z);
// 外层渐变圆环
const baseOut = {
type: 'gauge',
axisLabel: {
show: false,
},
axisLine: {
show: false,
lineStyle: {
color: gradientColors, // 因为使用axisLine填充的颜色之间会存在透明间隔, 所以使用axisTick来实现渐变平滑过渡.
},
},
axisTick: {
length: 4,
lineStyle: {
color: 'auto',
width: 2, // 调整width和splitNumber来使渐变平滑过渡
},
splitNumber: 150, // 调整width和splitNumber来使渐变平滑过渡
},
splitLine: {
show: false,
lineStyle: {
color: 'auto',
},
},
detail: {
show: false,
},
};
// 内层刻度和指针
const baseInner = {
radius: '70%',
type: 'gauge',
axisLine: {
show: false,
lineStyle: {
color: gradientColors,
},
},
splitLine: {
length: 12,
lineStyle: {
color: 'auto',
},
},
axisTick: {
lineStyle: {
width: 2,
color: 'auto',
},
splitNumber: 10,
},
pointer: {
width: 5,
length: '70%',
},
axisLabel: {
formatter(value) {
const index = axisLabelOriginData.findIndex((item) => item === value);
return axisLabelData[index];
},
},
};
option = {
series: [
baseOut,
{
...baseInner,
...testData,
data: [value],
detail: {
// 只支持字符串和函数
formatter: String(testData.data[0]),
},
},
],
};