I have a d3 graph that I implemented in Canvas. The graphs performance is great in Chrome, but when I deploy it with Ionic(webview), zoom and pan redrawing are really staggering on android and even slower on iOS.
I originally developed the graph with SVG, but switched to canvas after being convinced that canvas would run smoother.
Setup 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 Initializing
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();
Draw Code
The canvas draw 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();
}
Zoom Code
My zoom code looks like this:
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;
}
}
The performance seems to increase when excluding the area path(not drawing it at all) from the canvas.
The code
I'm attaching a link to the repository
. To make it run do the following:
- git clone https://github.com/airasheed/canvas-d3-test.git
- npm install
- ionic serve <--for browser to see graph
- ionic cordova build ios/android <--choose your testing platform
- OR - ionic cordova emulate android/ios
I'd like to know if its a code performance issue or if the amount of data i'm using is what's causing the issue.
For the first zoom level there are only 21 points, to plot which is surprising. It seems its staggering when redrawing.
Benchmarking
In chrome the line(data)
method takes .5ms, but in the iOS webview it can take anywhere from 15ms - 40ms. It looks like its lagging and not responsive.
Try to add the
Crosswalk WebView Engine
Plugin Performance seems to be improved :I have tested my Code with
Cordova Version : 6.5.0
I have Tested this in Android Physical Devices (Nexus -6) . You can find the Full Code with the code in the GithubRepo
Only case i need to verify in the Android version
4.4.4
aboveWill update the benchmark status of the
iOS
after installing in the device.Update :
Initially build was quite delay before installing the
WKwebview
. after installing theWKwebview
from the ionic-team (Original Apache repo is quite buggy and ionic-team has modified this plugin to fit for them so it is better to add from here ) repo i can make it work with smooth transitionSo Repo can be found here :
https://github.com/Webrusterkk/Canvas-With-ionic
ionic-WKWebview Cordova Plugin :
https://github.com/ionic-team/cordova-plugin-wkwebview-engine