使用Chart Js为正负值的不同折线图边框颜色。

huangapple go评论110阅读模式
英文:

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来实现segments,对于具有相同符号的两端的所有线段都运行良好,配置对象如下:

datasets:[{
    data: ....
    .......
    segment: {
        borderColor: (ctx) =&gt; {
            return getBorderColor(ctx);
        }
    },
}]

实际计算在函数getBorderColor中实现(略作修改以简化下面介绍我的扩展):

const getBorderColor = 
        (ctx, colPositive = &quot;green&quot;, colNegative = &quot;red&quot;) =&gt; {
    return ctx.p0.parsed.y &gt;= 0 ? colPositive : colNegative;
};

为了使其适应与x轴相交的线段,我找到的解决方案是将颜色设置为LinearGradient,具有由该表描述的四个color stops

      point:    p0       x轴交点      p1
coordinates:  (x0, y0)        (_, 0)           (x1, y1)
   fraction:     0             frac               1
      color:   green ------&gt; green|red --------&gt; red

其中frac可以从两点的坐标轻松计算。可以看到这实际上不是视觉梯度,从“green”到“red”的颜色变化(作为示例使用)发生在相同点,即线段与x轴的交点。

以下是带有注释的代码:

const getBorderColor = (ctx, colPositive = &quot;green&quot;, colNegative = &quot;red&quot;) =&gt; {
    if (ctx.p0.parsed.y * ctx.p1.parsed.y &lt; 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 ?? &quot;x&quot;,
            yAxisId = dataset.yAxisId ?? &quot;y&quot;,
            //将值转换为像素
            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 &gt; 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 &gt;= 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 segments and it worked fine for all segments that had both ends of the same sign; the config object was

datasets:[{
    data: ....
    .......
    segment: {
        borderColor: (ctx) =&gt; {
            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 = &quot;green&quot;, colNegative = &quot;red&quot;) =&gt; {
    return ctx.p0.parsed.y &gt;= 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 ------&gt; green|red --------&gt; 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 = &quot;green&quot;, colNegative = &quot;red&quot;) =&gt; {
    if (ctx.p0.parsed.y * ctx.p1.parsed.y &lt; 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 ?? &quot;x&quot;,
            yAxisId = dataset.yAxisId ?? &quot;y&quot;,
            //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 &gt; 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 &gt;= 0 ? colPositive : colNegative;
};

and the fork of the original codesandbox.

huangapple
  • 本文由 发表于 2023年5月10日 13:44:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76215225.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定