D3 Canvas graph zooming and panning slow redraw in

2019-06-22 07:07发布

问题:

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:

  1. git clone https://github.com/airasheed/canvas-d3-test.git
  2. npm install
  3. ionic serve <--for browser to see graph
  4. ionic cordova build ios/android <--choose your testing platform
  5. 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.

回答1:

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 above

Will update the benchmark status of the iOS after installing in the device.

Update :

Initially build was quite delay before installing the WKwebview. after installing the WKwebview 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 transition

So 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