ChartJS 为什么在对数据点应用多种颜色时得到错误的背景颜色?

cwdobuhd  于 2023-03-02  发布在  Chart.js
关注(0)|答案(2)|浏览(177)

我想显示一个不同的颜色为负数。正如你可以看到,从这张图片的工具提示显示正确的颜色,但实际的背景本身不是。
我试过更新图表,预设数组中的颜色,将颜色应用到borderColor,但是都不起作用。这可能是一个bug。

积极

否定

这是我的代码。

const paybackChartConfig = {
type: 'line',
data: {
    datasets: [{
        data: yData,
        backgroundColor: function (context) {
            const index = context.dataIndex;
            const value = context.dataset.data[index];
            return value < 0 ? 'rgba(54, 162, 235, 1)' : 'rgba(53, 122, 135, 1)';
        },
        borderWidth: 3,
        radius: 0,
        tension: 0.5,
        fill: true,
    }],
    labels: xData,
},
options: {
    scales: {
        x: {
            display: false,
            maxTicks: 15,
            grid: {
                display: false
            },
            title: {
                display: true,
                text: 'Year'
            }
        },
        y: {
            display: false,
            maxTicksLimit: 5,
            grid: {
                display: false
            },
            title: {
                display: false,
                text: 'Return'
            }
        }
    },
    plugins: {
        legend: {
            display: false
        },
        tooltip: {
            callbacks: {
                label: function (context) {
                    let label = context.dataset.label || '';

                    if (label) {
                        label += ': ';
                    }
                    if (context.parsed.y !== null) {
                        label += context.parsed.y + '%';
                    }

                    return label;
                }
            }
        }
    },
    interaction: {
        intersect: false,
        mode: 'index',
    },
    spanGaps: true,
},
plugins: [{
    afterDraw: chart => {
        if (chart.tooltip?._active?.length) {
            let x = chart.tooltip._active[0].element.x;
            let yAxis = chart.scales.y;
            let ctx = chart.ctx;
            ctx.save();
            ctx.beginPath();
            ctx.moveTo(x, yAxis.top);
            ctx.lineTo(x, yAxis.bottom);
            ctx.lineWidth = 1;
            ctx.strokeStyle = 'rgba(0, 0, 0, 0.4)';
            ctx.stroke();
            ctx.restore();
        }
    },
}],
};

const payBackChart = new Chart(document.getElementById('solarPaybackChart'), paybackChartConfig);
wbgh16ku

wbgh16ku1#

如本文文档中所述,您可以将fill指定为对象,而不是将其设置为true,这样您可以告诉chart.js使用不同的颜色填充特定值以上的内容,如下所示。

var ctx = document.getElementById("chart").getContext("2d");
var myLine = new Chart(ctx, {
  type: 'line',
  data: {
    labels: ["label1", "label2", "label3", "label4"],
    datasets: [{
      label: 'Demo',
      backgroundColor: function(context) {
        const index = context.dataIndex;
        const value = context.dataset.data[index];
        return value < 0 ? 'rgba(54, 162, 235, 1)' : 'rgba(53, 122, 135, 1)';
      },
      fill: {
        target: {
          value: 0
        },
        below: 'rgba(54, 162, 235, 1)',
        above: 'rgba(53, 122, 135, 1)'
      },
      data: [-2, -3, 4, 6],
    }]
  },
  options: {},
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.2.0/chart.umd.js"></script>
<canvas id="chart"></canvas>
ulydmbyx

ulydmbyx2#

感谢jordanwillis。他的codepen帮助我解决了这个问题。当然,解决这个问题是手动完成的,并有下面代码中的解释。

// decimal rounding algorithm
// see: https://plnkr.co/edit/uau8BlS1cqbvWPCHJeOy?p=preview
var roundNumber = function (num, scale) {
  var number = Math.round(num * Math.pow(10, scale)) / Math.pow(10, scale);
  if(num - number > 0) {
    return (number + Math.floor(2 * Math.round((num - number) * Math.pow(10, (scale + 1))) / 10) / Math.pow(10, scale));
  } else {
    return number;
  }
};

// save the original line element so we can still call it's 
// draw method after we build the linear gradient
var origLineElement = Chart.elements.Line;

// define a new line draw method so that we can build a linear gradient
// based on the position of each point
Chart.elements.Line = Chart.Element.extend({
  draw: function() {
    var vm = this._view;
    var backgroundColors = this._chart.controller.data.datasets[this._datasetIndex].backgroundColor;
    var points = this._children;
    var ctx = this._chart.ctx;
    var minX = points[0]._model.x;
    var maxX = points[points.length - 1]._model.x;
    var linearGradient = ctx.createLinearGradient(minX, 0, maxX, 0);

    // iterate over each point to build the gradient
    points.forEach(function(point, i) {
      // `addColorStop` expects a number between 0 and 1, so we
      // have to normalize the x position of each point between 0 and 1
      // and round to make sure the positioning isn't too percise 
      // (otherwise it won't line up with the point position)
      var colorStopPosition = roundNumber((point._model.x - minX) / (maxX - minX), 2);

      // special case for the first color stop
      if (i === 0) {
        linearGradient.addColorStop(0, backgroundColors[i]);
      } else {
        // only add a color stop if the color is different
        if (backgroundColors[i] !== backgroundColors[i-1]) {
          // add a color stop for the prev color and for the new color at the same location
          // this gives a solid color gradient instead of a gradient that fades to the next color
          linearGradient.addColorStop(colorStopPosition, backgroundColors[i - 1]);
          linearGradient.addColorStop(colorStopPosition, backgroundColors[i]);
        }
      }
    });

    // save the linear gradient in background color property
    // since this is what is used for ctx.fillStyle when the fill is rendered
    vm.backgroundColor = linearGradient;

    // now draw the lines (using the original draw method)
    origLineElement.prototype.draw.apply(this);
  }               
});

// we have to overwrite the datasetElementType property in the line controller
// because it is set before we can extend the line element (this ensures that 
// the line element used by the chart is the one that we extended above)
Chart.controllers.line = Chart.controllers.line.extend({
  datasetElementType: Chart.elements.Line,
});

// the labels used by the chart
var labels = ["Year 1", "Year 2", "Year 3", "Year 4", "Year 5"];

// the line chart point data
var lineData = [-50, -25, -6.04, 24.98, 50];

// colors used as the point background colors as well as the fill colors
var fillColors = [];
lineData.forEach((l)=> {
  fillColors.push(l < 0 ? 'rgba(54, 162, 235, 1)' : 'rgba(53, 122, 135, 1)');
});

// get the canvas context and draw the chart
var ctx = document.getElementById("solarPaybackChart").getContext("2d");
var myLine = new Chart(ctx, {
  type: 'line',
  data: {
    labels: labels,
    datasets: [{
      label: '',
      backgroundColor: fillColors, // now we can pass in an array of colors (before it was only 1 color)
      pointBackgroundColor: fillColors,
      fill: true,
      data: lineData,
    }]
  },
  options: {
    tooltips: {
        callbacks: {
            label: function (context) {
                let label = '';

                if (context.yLabel !== null) {
                    label += context.yLabel + '%';
                }

                return label;
            }
        }
    },
    scales: {
        x: {
            display: false,
            maxTicks: 15,
            grid: {
                display: false
            },
            title: {
                display: true,
                text: 'Year'
            }
        },
        y: {
            display: false,
            maxTicksLimit: 5,
            grid: {
                display: false
            },
            title: {
                display: false,
                text: 'Return'
            }
        }
    },
    plugins: {
        legend: {
            display: false
        },
        tooltips: {
            callbacks: {
                label: function (context) {
                    let label = '';

                    if (context.yLabel !== null) {
                        label += context.yLabel + '%';
                    }

                    return label;
                }
            }
        },
    },
    interaction: {
        intersect: false,
        mode: 'index',
    },
    spanGaps: true,
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.js"></script>
<canvas id="solarPaybackChart"></canvas>

上面的代码是用Chart.js 2.x实现的,不知道在Chart.js 3.x中是怎么实现的

相关问题