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.