[Chartjs]-Add an image as background in ChartJS chart area [Not to canvas]

2👍

Best approach, drawing it on the canvas

The best way to achieve this is by drawing it directly on the canvas since you have direct access and control over the pixels so you are sure it is aligned pixel perfect.

Extending on uminders answer, but making it so it only draws within the chart area:

const myChart = new Chart('myChart', {
  type: 'bubble',
  plugins: [{
    beforeDraw: chart => {
      var ctx = chart.ctx;
      ctx.save();
      const image = new Image();
      image.src = 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/Stack_Overflow_logo.svg/2560px-Stack_Overflow_logo.svg.png';
      ctx.drawImage(image, chart.chartArea.left, chart.chartArea.top, chart.chartArea.width, chart.chartArea.height);
      ctx.restore();
    }
  }],
  data: {
    datasets: [{
      label: 'My Dataset',
      data: [{
          x: 0,
          y: 12,
          r: 10
        },
        {
          x: 1,
          y: 19,
          r: 12
        },
        {
          x: 2,
          y: 3,
          r: 8
        },
        {
          x: 3,
          y: 5,
          r: 7
        },
        {
          x: 4,
          y: 2,
          r: 4
        },
        {
          x: 5,
          y: 3,
          r: 8
        }
      ],
      backgroundColor: [
        'rgba(255, 99, 132, 0.4)',
        'rgba(54, 162, 235, 0.4)',
        'rgba(255, 206, 86, 0.4)',
        'rgba(75, 192, 192, 0.4)',
        'rgba(153, 102, 255, 0.4)',
        'rgba(255, 159, 64, 0.4)'
      ],
      borderColor: [
        'rgba(255,99,132,1)',
        'rgba(54, 162, 235, 1)',
        'rgba(255, 206, 86, 1)',
        'rgba(75, 192, 192, 1)',
        'rgba(153, 102, 255, 1)',
        'rgba(255, 159, 64, 1)'
      ]
    }]
  },
  options: {
    responsive: true,
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.8.0/chart.js"></script>
<canvas id="myChart" height="80"></canvas>

Alternative approach without drawing on the canvas

If you really don’t want to draw it on the canvas but use a normal img element, you can use an absolute position and use a custom plugin to position it correctly, downside is that it seems that canvas pixels are different from CSS so they cant be used 1:1 and have to be approximated which may lead to slight alignment issues:

const myChart = new Chart('myChart', {
  type: 'bubble',
  plugins: [{
    beforeDraw: chart => {
      const image = document.getElementById('img')
      image.width = chart.chartArea.width;
      image.height = chart.chartArea.height;
      image.style.left = `${chart.chartArea.left*1.3}px`;
      image.style.top = `${chart.chartArea.top*1.3}px`;
    }
  }],
  data: {
    datasets: [{
      label: 'My Dataset',
      data: [{
          x: 0,
          y: 12,
          r: 10
        },
        {
          x: 1,
          y: 19,
          r: 12
        },
        {
          x: 2,
          y: 3,
          r: 8
        },
        {
          x: 3,
          y: 5,
          r: 7
        },
        {
          x: 4,
          y: 2,
          r: 4
        },
        {
          x: 5,
          y: 3,
          r: 8
        }
      ],
      backgroundColor: [
        'rgba(255, 99, 132, 0.4)',
        'rgba(54, 162, 235, 0.4)',
        'rgba(255, 206, 86, 0.4)',
        'rgba(75, 192, 192, 0.4)',
        'rgba(153, 102, 255, 0.4)',
        'rgba(255, 159, 64, 0.4)'
      ],
      borderColor: [
        'rgba(255,99,132,1)',
        'rgba(54, 162, 235, 1)',
        'rgba(255, 206, 86, 1)',
        'rgba(75, 192, 192, 1)',
        'rgba(153, 102, 255, 1)',
        'rgba(255, 159, 64, 1)'
      ]
    }]
  },
  options: {
    responsive: true,
  }
});
#img {
  position: absolute;
  z-index: -1/* Draw image behind the canvas */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.8.0/chart.js"></script>
<img id="img" src="https://upload.wikimedia.org/wikipedia/commons/thumb/0/02/Stack_Overflow_logo.svg/2560px-Stack_Overflow_logo.svg.png" />
<canvas id="myChart" height="80"></canvas>

0👍

The Plugin Core API offers a range of hooks that can be used for performing custom code. You can use the beforeDraw for example to draw the images through CanvasRenderingContext2D.drawImage(). But this effectively draws the image directly on the canvas.

plugins: [{
  beforeDraw: chart => {
    var ctx = chart.chart.ctx;
    ctx.save();
    var image = new Image();
    image.src = 'https://i.stack.imgur.com/S7tJH.png';
    imageSize = 250;
    ctx.drawImage(image, chart.chart.width / 2 - imageSize / 2, chart.chart.height / 2 - imageSize / 2, imageSize, imageSize);
    ctx.restore();
  }
}],

Please have a look at the runnable code snippet below.

var myChart = new Chart('myChart', {
  type: 'bubble',
  plugins: [{
    beforeDraw: chart => {
      var ctx = chart.chart.ctx;
      ctx.save();
      var image = new Image();      
      image.src = 'https://i.stack.imgur.com/S7tJH.png';      
      imageSize = 250;
      ctx.drawImage(image, chart.chart.width / 2 - imageSize / 2, chart.chart.height / 2 - imageSize / 2, imageSize, imageSize);
      ctx.restore();
    }
  }],
  data: {
    datasets: [{
      label: 'My Dataset',
      data: [
        { x: "05:22", y: 12, r: 10 },
        { x: "12:13", y: 19, r: 12  },
        { x: "13:45", y: 3, r: 8  },
        { x: "18:31", y: 5, r: 7  },
        { x: "19:05", y: 2, r: 4  },
        { x: "22:55", y: 3, r: 8  }
      ],
      backgroundColor: [
        'rgba(255, 99, 132, 0.4)',
        'rgba(54, 162, 235, 0.4)',
        'rgba(255, 206, 86, 0.4)',
        'rgba(75, 192, 192, 0.4)',
        'rgba(153, 102, 255, 0.4)',
        'rgba(255, 159, 64, 0.4)'
      ],
      borderColor: [
        'rgba(255,99,132,1)',
        'rgba(54, 162, 235, 1)',
        'rgba(255, 206, 86, 1)',
        'rgba(75, 192, 192, 1)',
        'rgba(153, 102, 255, 1)',
        'rgba(255, 159, 64, 1)'
      ]
    }]
  },
  options: {
    responsive: true,
    legend: {
        display: false
    },
    scales: {      
      xAxes: [{     
        type: 'time',
        time: {
          parser: 'HH:mm',
          unit: 'hour',
          stepSize: 1,
          displayFormats: {
            hour: 'HH:mm'   
          },          
          tooltipFormat: 'HH:mm'          
        },
        ticks: {
          min: '00:00',
          max: '24:00',
          callback: (value, index) => index == 24 ? '24:00' : value
        }
      }],
      yAxes: [{
        ticks: {
          beginAtZero: true,
          stepSize: 5
        }
      }]
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.min.js"></script>
<canvas id="myChart" height="80"></canvas>

Leave a comment