英文:
Different Line Chart border colors for positive and negative values using Chart Js
问题
我已使用Charts JS创建了折线图,但无法应用折线颜色变化。期望是:如果y值小于0(零),折线应为红色;如果大于0(零),则为绿色。
我已经在这里分叉了我的代码: codesandbox。
我尝试使用数据集段的borderColor;如果y值小于0,我将颜色设置为红色,否则如果y值大于0,颜色为绿色,但结果不如预期。
在数据集中,我有{x: 42380, y: 负值(接近零)}
,接着是{x: 42500, y: 正值(非常接近零的第一个正值)}
;这两点之间的段显示为红色,尽管从y=-185到零的负部分比从零到y=2000的正部分短得多。
英文:
I have created a line chart using charts JS, but I am unable to apply the line color change. Expectation should be: if the y value is less than 0 (zero) the line should be red and if it's greater than 0(zero) green.
I have forked my code here: codesandbox.
I have tried to use dataset segment borderColor; if y value <0 I set the color red, else if y value >0 the color is green, but it does not work as expected.
In the dataset I have {x: 42380, y: negative value (which is near by zero)}
followed by {x: 42500, y: POSITIVE value(which very first positive value)}]
; the segment between these two points is showing red
despite the fact that the negative part from y = -185 to zero, is much shorter than the positive part, from zero to y = 2000
答案1
得分: 1
更改线条颜色的解决方案取决于点的符号,已通过设置borderColor
来实现segment
s,对于具有相同符号的两端的所有线段都运行良好,配置对象如下:
datasets:[{
data: ....
.......
segment: {
borderColor: (ctx) => {
return getBorderColor(ctx);
}
},
}]
实际计算在函数getBorderColor
中实现(略作修改以简化下面介绍我的扩展):
const getBorderColor =
(ctx, colPositive = "green", colNegative = "red") => {
return ctx.p0.parsed.y >= 0 ? colPositive : colNegative;
};
为了使其适应与x轴相交的线段,我找到的解决方案是将颜色设置为LinearGradient
,具有由该表描述的四个color stops:
point: p0 x轴交点 p1
coordinates: (x0, y0) (_, 0) (x1, y1)
fraction: 0 frac 1
color: green ------> green|red --------> red
其中frac
可以从两点的坐标轻松计算。可以看到这实际上不是视觉梯度,从“green”到“red”的颜色变化(作为示例使用)发生在相同点,即线段与x轴的交点。
以下是带有注释的代码:
const getBorderColor = (ctx, colPositive = "green", colNegative = "red") => {
if (ctx.p0.parsed.y * ctx.p1.parsed.y < 0) {
// 如果线段从p0到p1改变符号
const x0 = ctx.p0.parsed.x,
x1 = ctx.p1.parsed.x,
y0 = ctx.p0.parsed.y,
y1 = ctx.p1.parsed.y,
dataset = ctx.chart.data.datasets[ctx.datasetIndex],
//确定用于数据集的正确轴
xAxisId = dataset.xAxisId ?? "x",
yAxisId = dataset.yAxisId ?? "y",
//将值转换为像素
x0px = ctx.chart.scales[xAxisId].getPixelForValue(x0),
x1px = ctx.chart.scales[xAxisId].getPixelForValue(x1),
y0px = ctx.chart.scales[yAxisId].getPixelForValue(y0),
y1px = ctx.chart.scales[yAxisId].getPixelForValue(y1);
// 从p0到p1创建渐变
const gradient = ctx.chart.ctx.createLinearGradient(x0px, y0px, x1px, y1px);
// 计算frac - 线段从p0到与x轴相交的点的部分的相对长度
const frac = Math.abs(y0) / (Math.abs(y0) + Math.abs(y1));
// 在线段两端设置颜色
const [col_p0, col_p1] =
y0 > 0 ? [colPositive, colNegative] : [colNegative, colPositive];
gradient.addColorStop(0, col_p0);
gradient.addColorStop(frac, col_p0);
gradient.addColorStop(frac, col_p1);
gradient.addColorStop(1, col_p1);
return gradient;
}
return ctx.p0.parsed.y >= 0 ? colPositive : colNegative;
};
以及修改版的原始codesandbox。
英文:
The solution for changing the color of the line depending on the sign of the points was already implemented by setting the borderColor
for segment
s and it worked fine for all segments that had both ends of the same sign; the config object was
datasets:[{
data: ....
.......
segment: {
borderColor: (ctx) => {
return getBorderColor(ctx);
}
},
}]
with the actual computation implemented in the function getBorderColor
(slightly modified from the original to simplify the introduction of my extension below):
const getBorderColor =
(ctx, colPositive = "green", colNegative = "red") => {
return ctx.p0.parsed.y >= 0 ? colPositive : colNegative;
};
To adapt this to work for segments that intersect the x axis, the solution I found was to set the color as a LinearGradient
with four color stops described by this table:
point: p0 x-axis intersection p1
coordinates: (x0, y0) (_, 0) (x1, y1)
fraction: 0 frac 1
color: green ------> green|red --------> red
where frac
is easy to compute from the coordinates of the two points. One can see this is not actually a visual gradient, the change of color from "green" to "red" (used as an example) is done at the same point, the intersection of the segment to the x axis.
Here's the code with explanations in comments:
const getBorderColor = (ctx, colPositive = "green", colNegative = "red") => {
if (ctx.p0.parsed.y * ctx.p1.parsed.y < 0) {
// if the segment changes sign from p0 to p1
const x0 = ctx.p0.parsed.x,
x1 = ctx.p1.parsed.x,
y0 = ctx.p0.parsed.y,
y1 = ctx.p1.parsed.y,
dataset = ctx.chart.data.datasets[ctx.datasetIndex],
//identify the correct axes used for the dataset
xAxisId = dataset.xAxisId ?? "x",
yAxisId = dataset.yAxisId ?? "y",
//transform values to pixels
x0px = ctx.chart.scales[xAxisId].getPixelForValue(x0),
x1px = ctx.chart.scales[xAxisId].getPixelForValue(x1),
y0px = ctx.chart.scales[yAxisId].getPixelForValue(y0),
y1px = ctx.chart.scales[yAxisId].getPixelForValue(y1);
// create gradient form p0 to p1
const gradient = ctx.chart.ctx.createLinearGradient(x0px, y0px, x1px, y1px);
// calculate frac - the relative length of the portion of the segment
// from p0 to the point where the segment intersects the x axis
const frac = Math.abs(y0) / (Math.abs(y0) + Math.abs(y1));
// set colors at the ends of the segment
const [col_p0, col_p1] =
y0 > 0 ? [colPositive, colNegative] : [colNegative, colPositive];
gradient.addColorStop(0, col_p0);
gradient.addColorStop(frac, col_p0);
gradient.addColorStop(frac, col_p1);
gradient.addColorStop(1, col_p1);
return gradient;
}
return ctx.p0.parsed.y >= 0 ? colPositive : colNegative;
};
and the fork of the original codesandbox.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论