Skip to content
Advertisement

D3 / Canvas: Axis not displayed

i have the following issue: I want to create a simple chart using the d3 library and the canvas rendering.

var width = 800;
var height = 600;

var data = [10, 15, 20, 25, 30];

var chart = d3.select("#chart")
            .append("canvas")
            .attr("width", width)
            .attr("height", height);

var scale = d3.scaleLinear()
              .domain([d3.min(data), d3.max(data)])
              .range([0, width - 100]);

var x_axis = d3.axisBottom()
               .scale(scale);

chart.append("group")
   .call(x_axis);

I don’t see anything in my browser – but, if I inspect the element, the Axis is ‘compiled’ in the code and I can hover over every tick in the code while the position in the DOM is marked. But, nothing to see.

There is already a chart written in d3 / canvas, but, the Axis are accessed to the canvas directly, like

const canvas = document.getElementById(this.id)
const context = canvas.getContext('2d')
// ..a lot of code here..
context.beginPath()
moveTo(0, height + tickSize)
lineTo(0, height - 0.5)
// ...

The goal is, to use d3 for that.

Is this possible only with svg?

Thanks in advance!

Advertisement

Answer

You would need to draw the axis manually on the <canvas> element. Here’s an example, with help from D3 and Canvas in 3 steps and D3 + Canvas demo:

// Grab a reference to the canvas element
let canvas = document.querySelector('canvas');
// And to its context
let ctx = canvas.getContext('2d');

// Define our margins and plot dimensions
let margins = {
  top: 20,
  bottom: 40,
  left: 30,
  right: 20
};
let width = canvas.width - margins.left - margins.right;
let height = canvas.height - margins.top - margins.bottom;

// Center the chart
ctx.translate(margins.left, margins.top);

// Your data
let data = [10, 15, 20, 25, 30];

// Create the x-scale
let x = d3.scaleBand()
  .domain(data)
  .rangeRound([0, width])
  .padding(0.1);

// Create the y-scale
let y = d3.scaleLinear()
  .domain([0, d3.max(data)])
  .rangeRound([height, 0]);

// Define y-ticks
var yTickCount = 10;
var yTicks = y.ticks(yTickCount);

// Draw the x-axis
ctx.beginPath();
x.domain().forEach(d => {
  ctx.moveTo(x(d) + x.bandwidth() / 2, height);
  ctx.lineTo(x(d) + x.bandwidth() / 2, height + 6);
});
ctx.strokeStyle = '#000';
ctx.stroke();

// And apply the x-axis labels
ctx.textAlign = "center";
ctx.textBaseline = "top";
x.domain().forEach((d, i) => {
  ctx.fillText(i, x(d) + x.bandwidth() / 2, height + 6);
});
ctx.textAlign = "center";
ctx.textBaseline = "bottom";
x.domain().forEach((d, i) => {
  ctx.fillText(d, x(d) + x.bandwidth() / 2, y(d));
});
ctx.strokeStyle = "#000";
ctx.stroke();

// Now draw the y-axis
yTicks.forEach(d => {
  ctx.moveTo(0, y(d) + 0.5);
  ctx.lineTo(-6, y(d) + 0.5);
});
ctx.strokeStyle = "#000";
ctx.stroke();

// And add the y-axis ticks and labels
ctx.textAlign = "right";
ctx.textBaseline = "middle";
yTicks.forEach(d => {
  ctx.fillText(d, -9, y(d));
});
ctx.beginPath();
ctx.lineTo(0.5, 0.5);
ctx.lineTo(0.5, height + 0.5);
ctx.strokeStyle = "#000";
ctx.stroke();

ctx.save();
ctx.rotate(-Math.PI / 2);
ctx.textAlign = "right";
ctx.textBasline = "top";
ctx.restore();

// Finally, draw the bars using your data
ctx.fillStyle = 'blue';
data.forEach((d, i) => {
  ctx.fillRect(x(d), y(d), x.bandwidth(), height - y(d));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<canvas id="canvas" height=300 width=600></canvas>

If you don’t have to use D3, you might want to look into a different charting library that’s designed especially for the canvas element, like CanvasJS.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement