reactjschartsreact-chartjsreact-chartjs-2

React-chartjs-2: Gap Between Red and Green Colors


I'm using the react-chartjs-2 library to render a single line chart with red and green segments representing different conditions. I've followed the provided documentation and code examples to create the chart, but I'm facing an issue where there's an unexpected gap between the red and green segments. Despite trying various configurations, the gap persists.

this is my code:

import React from "react";
import { Line } from "react-chartjs-2";
import { Chart as ChartJS, registerables } from "chart.js";
import "chartjs-adapter-moment";
ChartJS.register(...registerables);

const data = [
  {
    t: 1692691200000,
    o: 209.9,
    h: 210.5,
    c: 210.1,
    v: 24849.905,
    l: 209.7,
    i: 0,
    _id: "64e592b6ed350eac66f1279e"
  },
  {
    t: 1692694800000,
    o: 210.1,
    h: 210.9,
    c: 209.8,
    v: 17912.358,
    l: 209.7,
    i: 0,
    _id: "64e592b6ed350eac66f1279f"
  },
  {
    t: 1692698400000,
    o: 209.8,
    h: 210.1,
    c: 209.7,
    v: 15451.882,
    l: 209.3,
    i: 0,
    _id: "64e592b6ed350eac66f127a0"
  },
  {
    t: 1692702000000,
    o: 209.8,
    h: 209.8,
    c: 209.1,
    v: 14630.712,
    l: 208.8,
    i: 0,
    _id: "64e592b6ed350eac66f127a1"
  },
  {
    t: 1692705600000,
    o: 209.1,
    h: 209.9,
    c: 209.2,
    v: 14840.568,
    l: 209,
    i: 0,
    _id: "64e592b6ed350eac66f127a2"
  },
  {
    t: 1692709200000,
    o: 209.2,
    h: 209.4,
    c: 206.9,
    v: 76291.989,
    l: 205.9,
    i: 0,
    _id: "64e592b6ed350eac66f127a3"
  },
  {
    t: 1692712800000,
    o: 207,
    h: 207.8,
    c: 206.9,
    v: 55066.85,
    l: 205.9,
    i: 0,
    _id: "64e592b6ed350eac66f127a4"
  },
  {
    t: 1692716400000,
    o: 206.9,
    h: 208.1,
    c: 205.6,
    v: 70269.986,
    l: 204.3,
    i: 0,
    _id: "64e592b6ed350eac66f127a5"
  },
  {
    t: 1692720000000,
    o: 205.5,
    h: 207.3,
    c: 207.2,
    v: 107602.133,
    l: 203.4,
    i: 0,
    _id: "64e592b6ed350eac66f127a6"
  },
  {
    t: 1692723600000,
    o: 207.1,
    h: 207.2,
    c: 205.9,
    v: 59274.354,
    l: 204.9,
    i: 0,
    _id: "64e592b6ed350eac66f127a7"
  },
  {
    t: 1692727200000,
    o: 206,
    h: 210.4,
    c: 208.8,
    v: 81805.016,
    l: 204.9,
    i: 0,
    _id: "64e592b6ed350eac66f127a8"
  },
  {
    t: 1692730800000,
    o: 208.8,
    h: 209.9,
    c: 208.4,
    v: 29905.367,
    l: 208,
    i: 0,
    _id: "64e592b6ed350eac66f127a9"
  },
  {
    t: 1692734400000,
    o: 208.3,
    h: 209.4,
    c: 209.3,
    v: 18693.145,
    l: 208.3,
    i: 0,
    _id: "64e592b6ed350eac66f127aa"
  },
  {
    t: 1692738000000,
    o: 209.3,
    h: 209.5,
    c: 206.8,
    v: 60334.243,
    l: 204.7,
    i: 0,
    _id: "64e592b6ed350eac66f127ab"
  },
  {
    t: 1692741600000,
    o: 206.8,
    h: 209.4,
    c: 208.7,
    v: 32667.587,
    l: 206.4,
    i: 0,
    _id: "64e592b6ed350eac66f127ac"
  },
  {
    t: 1692745200000,
    o: 208.8,
    h: 213,
    c: 210.9,
    v: 62314.118,
    l: 208.7,
    i: 0,
    _id: "64e592b6ed350eac66f127ad"
  },
  {
    t: 1692748800000,
    o: 210.9,
    h: 215.6,
    c: 213.7,
    v: 98698.808,
    l: 210.8,
    i: 0,
    _id: "64e592b6ed350eac66f127ae"
  },
  {
    t: 1692752400000,
    o: 213.8,
    h: 214.5,
    c: 214.1,
    v: 19970.85,
    l: 213.5,
    i: 0,
    _id: "64e592b6ed350eac66f127af"
  },
  {
    t: 1692756000000,
    o: 214.1,
    h: 214.2,
    c: 213.4,
    v: 18173.768,
    l: 213.1,
    i: 0,
    _id: "64e592b6ed350eac66f127b0"
  },
  {
    t: 1692763200000,
    o: 212.6,
    h: 213.7,
    c: 213.6,
    v: 10753.479,
    l: 212.3,
    i: 0,
    _id: "64e592b6ed350eac66f127b1"
  },
  {
    t: 1692766800000,
    o: 213.5,
    h: 213.7,
    c: 213.6,
    v: 169.645,
    l: 213.5,
    i: 0,
    _id: "64e592b6ed350eac66f127b2"
  },
  {
    t: 1692770400000,
    o: 213.9,
    h: 214,
    c: 214,
    v: 1.285,
    l: 213.9,
    i: 0,
    _id: "64e5a0c81a0fcedd5b1b6393"
  },
  {
    t: 1692774000000,
    o: 214.8,
    h: 215.3,
    c: 215.2,
    v: 4903.512,
    l: 214.5,
    i: 0,
    _id: "64e5b1561a0fcedd5b312de7"
  },
  {
    t: 1692777600000,
    o: 216.4,
    h: 216.4,
    c: 216.4,
    v: 0,
    l: 216.4,
    i: 0,
    _id: "64e5bce91a0fcedd5b40f590"
  },
  {
    t: 1692781200000,
    o: 215.3,
    h: 215.3,
    c: 215.3,
    v: 51.575,
    l: 215.3,
    i: 0,
    _id: "64e5caf71a0fcedd5b5460b9"
  }
];

const App = () => {
  // Extracting timestamps and values from the data
  const timestamps = data.map((entry) => entry.t);
  const values = data.map((entry) => entry.c); // Using 'c' for closing prices, change it if needed
  console.log("🚀 ~ file: index.tsx:264 ~ ChartComponent ~ values:", values);

  // Defining green and red colors for the chart
  const greenColor = "rgba(0, 255, 0, 0.6)";
  const redColor = "rgba(255, 0, 0, 0.6)";

  // Creating datasets for green and red segments
  const datasets = [
    {
      label: "Close",
      data: values.map((value) => (value >= values[0] ? value : null)),
      borderColor: greenColor,
      backgroundColor: greenColor,
      fill: false, // Filling area above the line
      tension: 0.3,
      pointRadius: 0
    },
    {
      label: "Close",
      data: values.map((value) => (value < values[0] ? value : null)),
      borderColor: redColor,
      backgroundColor: redColor,
      fill: false, // Filling area below the line
      pointRadius: 0,
      tension: 0.3
    }
  ];

  const chartData = {
    labels: timestamps,
    datasets: datasets
  };

  const options = {
    scales: {
      x: {
        display: false // Hide the x-axis
      },
      y: {
        display: false
      }
    },
    plugins: {
      legend: {
        display: false // Hide the legend
      }
    }
  };
  return (
    <div>
      <Line data={chartData} options={options} />
    </div>
  );
};

export default App;

SandBox Demo

enter image description here


Solution

  • By splitting the data into two datasets you only define a segment to be red if both end values are less than value[0] and green if both end values are greater or equal than value[0]. You don't say what the color should be if one end is less and the other is greater - these mixed segments don't exist and hence the gaps.

    The simplest way to do that is to keep all data in one dataset (so you also have mixed segments) and use the segment scriptable properties.

    With

      const options = {
        segment: {
          borderColor: function ({ p0DataIndex, p1DataIndex }) {
            return values[p0DataIndex] >= values[0] || values[p1DataIndex] >= values[0] ? 
                greenColor : redColor;
          },
        },
        // ....... the other options
     }
    

    you set the segment to have color green if at least the value of one end is greater than value[0] and red if both are less. So the mixed ends segments will be green. Of course, other logics can be implemented, and other visual properties can be similarly defined, like backgroundColor.

    Here's a fork of the sandbox with that implemented.

    Another technique that might be useful is to split the mixed segments at exactly the level of value[0], using a gradient:

    datesets:[{
       // .....
       borderColor: function (context) {
          const chart = context.chart;
          const { ctx, chartArea } = chart;
          if (!chartArea) {
            // This case happens on initial chart load
            return;
          }
          const y0Px = chart.scales.y.getPixelForValue(values[0]),
            y0Fraction = (chartArea.bottom - y0Px) / (chartArea.bottom - chartArea.top);
          const gradient = ctx.createLinearGradient(
              0,
              chartArea.bottom,
              0,
              chartArea.top
          );
          gradient.addColorStop(0, redColor);
          gradient.addColorStop(y0Fraction, redColor);
          gradient.addColorStop(y0Fraction, greenColor);
          gradient.addColorStop(1, greenColor);
    
          return gradient;
        },
    ]
    

    The line will be red from the bottom of the chart to the level of value[0] and green above that level. Here's the codesandbox fork with that piece of code.