简体   繁体   中英

How to maintain chartjs / ng2-charts gradient on window resize?

I had applied some gradient rule to my chartjs chart. And it looks great as you can see on the below在此处输入图像描述

However, when the browser window is resized (ie width of window is smaller), the gradient is ruined (bottom blue colors disappeared). Screenshot:

在此处输入图像描述

I want to maintain the graph's gradient with all values and fit the different widths (responsive). Is there any way to do that? Here is what I had tried but didn't work:

.TS File

    ngAfterViewInit() {
        const ctx = (<HTMLCanvasElement>this.myChart.nativeElement).getContext('2d');
        const purple_orange_gradient = ctx.createLinearGradient(0, 200, 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");


        const bar_chart = new Chart(ctx, {
          type: "horizontalBar",
          data: {
            labels: []=this.histogramLabels.reverse(),
            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: []=this.histogramGraphData
            }]
          },
        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"
                    }
                }]
            }
        }
        }

        )

     }

.HTML

<div class="row my-2">
   <div class="col-md-6">
      <canvas id=”myChart” #myChart height="130"></canvas>
   </div>
</div>

示例 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 (ie 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.

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()
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM