I have a d3 graph that I implemented in Canvas. Graphics performance is great in Chrome, but when I deploy it using Ionic (web browsing), zooming and panning redraws on android and even slower on iOS.
I initially designed the graph with SVG, but switched to canvas, making sure the canvas worked more smoothly.
Installation code
HTML
<ion-header> <ion-navbar> <ion-title> Ionic Blank </ion-title> </ion-navbar> </ion-header> <ion-content padding> <canvas width="300" height="300"></canvas> </ion-content>
Canvas Initialization
mode = 'monthly'; canvas = document.querySelector("canvas"); context = canvas.getContext("2d"); margin = { top: 20, right: 20, bottom: 30, left: 50 }, width = canvas.width - margin.left - margin.right, height = canvas.height - margin.top - margin.bottom; var parseTime = d3.timeParse("%d-%b-%y"); // setup scales x = d3.scaleTime() .range([0, width]); x2 = d3.scaleTime().range([0, width]); y = d3.scaleLinear() .range([height, 0]); // setup domain x.domain(d3.extent(data, function (d) { return moment(d.usageTime); })); y.domain(d3.extent(data, function (d) { return d.kWh; })); x2.domain(x.domain()); // get day range dayDiff = daydiff(x.domain()[0], x.domain()[1]); // line generator line = d3.line() .x(function (d) { return x(moment(d.usageTime)); }) .y(function (d) { return y(d.kWh); }) .curve(d3.curveMonotoneX) .context(context); area = d3.area() .curve(d3.curveMonotoneX) .x(function (d) { return x(moment(d.usageTime)); }) .y0(height) .y1(function (d) { return y(d.kWh); }) .context(context); // zoom zoom = d3.zoom() .scaleExtent([1, dayDiff * 12]) .translateExtent([[0, 0], [width, height]]) .extent([[0, 0], [width, height]]) .on("zoom", zoomed); d3.select("canvas").call(zoom) context.translate(margin.left, margin.top); draw();
Drawing code
The canvas drawing code looks like this:
function draw() { // remove everything: context.clearRect(-margin.left, -margin.top, canvas.width, canvas.height); // draw axes: xAxis(); yAxis(); // save the context without a clip path context.save(); // create a clip path: context.beginPath() context.rect(0, 0, width, height); context.lineWidth = 0; context.strokeStyle = "none"; context.stroke(); context.clip(); // draw line in clip path line(data); context.lineWidth = 1.5; context.strokeStyle = "steelblue"; context.stroke(); context.beginPath(); area(data); context.fillStyle = 'steelblue'; context.strokeStyle = 'steelblue'; context.fill(); // restore without a clip path context.restore(); }
Scaling code
My scaling code is as follows:
function zoomed() { var t = d3.event.transform; x = t.rescaleX(x2); draw(); var diff = daydiff(x.domain()[0], x.domain()[1]); if (diff < 366 && diff > 120 && mode !== 'monthly') { mode = 'monthly'; data = monthlyData; y.domain(d3.extent(data, function (d) { return d.kWh; })); return; } if (diff <= 120 && diff > 2 && mode !== 'daily') { mode = 'daily'; data = dailyData; y.domain(d3.extent(data, function (d) { return d.kWh; })); return; } }
Productivity, apparently, increases if you exclude the path of the area (do not draw it at all) from the canvas.
Code
I am attaching a link to the repository . To start it, follow these steps:
I would like to know if the problem is with the code performance or if the amount of data that I use causes the problem.
For the first level of scaling, only 21 points , which may seem unexpected. It seems like he is stunned when redrawing.
Benchmarking
In chrome, the line(data) method takes .5ms, but in iOS web browsing it can take anywhere from 15ms to 40ms. He seems to be lagging and not responding.