如何在chart.js2中根据斜率是正还是负来更改折线图段的颜色?

llmtgqce  于 2023-04-06  发布在  Chart.js
关注(0)|答案(1)|浏览(113)

我目前正试图使一个股票跟踪图表(我知道它的基本,但我还在学习)在sveltekit.在目前的状态下,我没有问题生成一个基本的图形,其中线都是一种颜色,但我想与一个积极的斜率段是绿色和负斜率是紫色.
到目前为止,我已经尝试了2个实现

function generateGradient(ctx, stockData) {
    const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);

    for (let i = 1; i < stockData.length; i++) {
      const color = getLineColor(stockData[i - 1], stockData[i]);
      const position = i / (stockData.length - 1);
      gradient.addColorStop(position, color);
    }

    return gradient;
  }

  function initializeChart() {
    const ctx = canvas.getContext('2d');
    const chartLabels = stockData.map((item) => item.datetime.slice(0, 10));
    const chartData = stockData.map((item) => parseFloat(item.close));
    // const gradientStroke = generateGradient(ctx, stockData);

    var gradientStroke = ctx.createLinearGradient(500, 0, 100, 0);
    gradientStroke.addColorStop(0, "#80b6f4");
    gradientStroke.addColorStop(1, "#f49080");

    chart = new Chart(ctx, {
      type: 'line',
      data: {
        labels: chartLabels,
        datasets: [
          {
            label: 'Stock Price',
            data: chartData,
            // borderColor: getLineColor(),
            borderColor: gradientStroke,
            borderWidth: 3, // line width
            pointRadius: 2, // point radius
            fill: false,
          },
        ],
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
      },
    });
  }

这会根据线段是正还是负生成不同的梯度停止点,这将导致不稳定的梯度,但它只会导致chart.js中的错误。
我的第二个实现是

function generateSegmentedDatasets(stockData) {
        const datasets = [];
        for (let i = 1; i < stockData.length; i++) {
        const color = getLineColor(stockData[i - 1], stockData[i]);
        const data = Array(i - 1).fill(null).concat(stockData[i - 1].close, stockData[i].close).concat(Array(stockData.length - i - 1).fill(null));
        datasets.push({
            label: `Segment ${i}`,
            data,
            borderColor: color,
            borderWidth: 3,
            pointRadius: 2,
            fill: false,
            tension: 0,
        });
        }
        return datasets;
    }

function initializeChart() {
        const ctx = canvas.getContext('2d');
        const chartLabels = stockData.map((item) => item.datetime.slice(0, 10));
        const chartData = generateSegmentedDatasets(stockData);

        chart = new Chart(ctx, {
        type: 'line',
        data: {
            labels: chartLabels,
            datasets: chartData,
        },
        options: {
            responsive: true,
            maintainAspectRatio: false,
        },
   });
}

理论上,这应该会产生不同颜色的完全不同的线段,但我认为我对chartjs的了解不足以判断这是否是一个可行的解决方案,我只是一次性编写了所有代码,而没有进行测试(再次请对我放松,这是第一次项目)
这两个实现都遵循onmount函数,该函数从我存储在项目目录中的.json中获取数据,以便于使用。
这也是在sveltekit中完成的,它增加了一层额外的复杂性,但我怀疑这不是问题所在。

wsxa1bj1

wsxa1bj11#

Chart.js文档中有一个示例,展示了您希望使用segments完成的操作。
这里的关键是segment的定义(简化后去掉了skipped修饰符,只关注down修饰符):

segment: {
  borderColor: ctx => down(ctx, 'rgb(192,75,75)'),
},

及其相应的助手:

const down = (ctx, value) => ctx.p0.parsed.y > ctx.p1.parsed.y ? value : undefined;

对于数据集中的每个线段[p0, p1]down助手被调用。如果p0 > p1(即线段是倾斜的),那么助手将返回一个颜色值(在代码中是硬设置为rgb(192,75,75)的),并将其影响到borderColor属性中。
否则,helper函数将返回undefined,并且片段(倾斜 * 向上 *)将默认为其默认配置(在上面链接的示例中,为borderColor: 'rgb(75, 192, 192)')。
为了将所有这些放在Svelte中,我们需要做的就是声明一个canvas元素来保存我们的图表,使用bind:this获取对它的引用,并在onMount()中创建图表,以确保画布已经挂载并且其引用可用:

<script>
    import { onMount } from 'svelte';
    import Chart from 'chart.js/auto';
    let canvas;
    
    const down = (ctx, value) => ctx.p0.parsed.y > ctx.p1.parsed.y ? value : undefined;

    const genericOptions = {
        fill: false,
        interaction: {
            intersect: false
        },
        radius: 0,
    };

    const config = {
        type: 'line',
        data: {
            labels: ['A', 'B', 'C', 'D', 'E', 'F', 'G'],
            datasets: [{
                label: 'Price chart',
                data: [65, 59, 40, 48, 56, 57, 40],
                borderColor: 'rgb(75, 192, 192)',
                segment: {
                    borderColor: ctx => down(ctx, 'rgb(192,75,75)'),
                },
            }]
        },
        options: genericOptions
    };
    
    onMount(() => {
        const chart = new Chart(canvas, config);
    });
</script>

<div style="width: 800px;">
    <canvas bind:this={canvas}></canvas>
</div>

Demo REPL

相关问题