score:2

Accepted answer

example gif

HTML Canvas' createLinearGradient() depends on the y axis coordinates that you pass in as argument. You had passed in a static 200 every time (i.e. ctx.createLinearGradient(0, 200, 0, 20);).

That's why the gradient's steps remains the same everytime. For the gradient to update, you have to recalculate the height of the <canvas> element on window resize and pass it in to createLinearGradient() again.

You can accomplish this by:

  • Separating the block where you create the gradient into a separate function. eleHeight retrieves the height of the canvas element.
  generateGradient(){
    let eleHeight = this.myChart.nativeElement.offsetHeight;
    // console.log(eleHeight)
    let purple_orange_gradient: CanvasGradient = this.myChart.nativeElement.getContext('2d').createLinearGradient(0, eleHeight, 0, 20);
    purple_orange_gradient.addColorStop(0.1, "#000279");
    purple_orange_gradient.addColorStop(0.2, "#0000F2");
    purple_orange_gradient.addColorStop(0.3, "#0362FD");
    purple_orange_gradient.addColorStop(0.4, "#04D3FD");
    purple_orange_gradient.addColorStop(0.5, "#45FFB7");
    purple_orange_gradient.addColorStop(0.6, "#B7FF46");
    purple_orange_gradient.addColorStop(0.7, "#FFD401");
    purple_orange_gradient.addColorStop(0.8, "#FE6500");
    purple_orange_gradient.addColorStop(0.9, "#F30004");
    purple_orange_gradient.addColorStop(1, "#7E0100");

    return purple_orange_gradient;
  }
  • Add a onresize event handler to your containing <div> and generate the gradient again. You also need to programatically update the chart every time you make a change to re-render it.
<div style="display: block; max-height: 100%" (window:resize)="onResize($event)" >
...
</div>
  onResize(event?){
    // console.log("onResize");

    this.barChartData.forEach((d, i) => {
      d.backgroundColor = this.generateGradient();
    })

    this.chart.chart.update(); //update the chart to re-render it
  }
  • Update the barchartData's properties (that uses gradient) in ngAfterViewInit. We need to do this here because we only want the height of the <canvas> element with data populated. Without data populated, the element is much smaller.
  ngAfterViewInit(){
    this.barChartData.forEach((d, i) => {
      d.backgroundColor = this.generateGradient();
    });
    this.chart.chart.update(); //update the chart to re-render it
  }

Have a look at this Stackblitz example⚡⚡ I have created.

score:1

You have to change the gradient whenever your canvas is resizing. Took me a while to figure out a good structure to minimize lines of code and optimize performance. This is the best I could achieve.

There are exeptions when the chart.js onResize() fires though but I couldn't solve this issue completly bulletproof. But for simple resizes it should work.

Complete code (same code in JSBin with live preview):

let sData = {}
sData.labels = []
sData.data = []

const count = 50
for (let x = 0; x < count; x++) {
  sData.data.push(Math.floor(Math.random()*100))
  sData.labels.push(x)
}

const canvas = document.getElementById('chart')
const ctx = canvas.getContext("2d")

let purple_orange_gradient

function updateGradient() {
  let bottom = bar_chart.chartArea.bottom
  let top = bar_chart.chartArea.top
  purple_orange_gradient = ctx.createLinearGradient(0, bottom+top, 0, top)
  purple_orange_gradient.addColorStop(0.1, "#000279")
  purple_orange_gradient.addColorStop(0.2, "#0000F2")
  purple_orange_gradient.addColorStop(0.3, "#0362FD")
  purple_orange_gradient.addColorStop(0.4, "#04D3FD")
  purple_orange_gradient.addColorStop(0.5, "#45FFB7")
  purple_orange_gradient.addColorStop(0.6, "#B7FF46")
  purple_orange_gradient.addColorStop(0.7, "#FFD401")
  purple_orange_gradient.addColorStop(0.8, "#FE6500")
  purple_orange_gradient.addColorStop(0.9, "#F30004")
  purple_orange_gradient.addColorStop(1.0, "#7E0100")

  return purple_orange_gradient
}

const bar_chart = new Chart(ctx, {
  type: "horizontalBar",
  data: {
    labels: sData.labels,
    datasets: [{
      borderColor: purple_orange_gradient,
      pointBorderColor: purple_orange_gradient,
      pointBackgroundColor: purple_orange_gradient,
      pointHoverBackgroundColor: purple_orange_gradient,
      pointHoverBorderColor: purple_orange_gradient,
      pointBorderWidth: 10,
      pointHoverRadius: 10,
      pointHoverBorderWidth: 1,
      pointRadius: 3,
      fill: true,
      backgroundColor: purple_orange_gradient,
      borderWidth: 4,
      data: sData.data
    }]
  },
  options: {
    legend: {
      display: false,
      position: "bottom"
    },
    scales: {
      yAxes: [{
        ticks: {
          display: false,
          fontColor: "rgba(0,0,0,0.5)",
          fontStyle: "bold",
          beginAtZero: true,
          maxTicksLimit: 1,
          padding: 20,
        },
        gridLines: {
          drawTicks: false,
          display: false
        }
      }],
      xAxes: [{
        gridLines: {
          zeroLineColor: "transparent",
        },
        ticks: {
          padding: 20,
          beginAtZero: true,
          fontColor: "rgba(0,0,0,0.5)",
          fontStyle: "bold"
        }
      }]
    },
    onResize: function(chart, size) {
      // onResize gradient change
      changeGradient()
    }
  }
});

// Initial gradient change
changeGradient()


function changeGradient() {
  let newGradient = updateGradient()

  bar_chart.data.datasets[0].borderColor = newGradient
  bar_chart.data.datasets[0].pointBorderColor = newGradient
  bar_chart.data.datasets[0].pointBackgroundColor = newGradient
  bar_chart.data.datasets[0].pointHoverBackgroundColor = newGradient
  bar_chart.data.datasets[0].pointHoverBorderColor = newGradient
  bar_chart.data.datasets[0].backgroundColor = newGradient

  bar_chart.update()
}

Related Query

More Query from same tag