Improving D3 Performance with CANVAS and other Hacks

27
IMPROVING D3 PERFORMANCE WITH CANVAS AND OTHER HACKS WebTechCon 2015 2015-10-27

Transcript of Improving D3 Performance with CANVAS and other Hacks

Page 1: Improving D3 Performance with CANVAS and other Hacks

I M P R O V I N G D 3 P E R F O R M A N C E W I T H C A N VA S A N D O T H E R H A C K S

WebTechCon 2015 2015-10-27

Page 2: Improving D3 Performance with CANVAS and other Hacks

Philip Tellis @bluesmoon

https://github.com/lognormal/boomerang

http://www.soasta.com/mpulse/

Page 3: Improving D3 Performance with CANVAS and other Hacks

D 3 I S …

• A JavaScript library that maps Data to DOM Nodes

• Extended via layouts & plugins for rich data visualisations

• You still need to write code to draw things

• Fast on its own, but you can easily make it sluggish

• BSD Licensed — http://d3js.org/

Page 4: Improving D3 Performance with CANVAS and other Hacks

H T T P S : / / G I T H U B . C O M / M B O S T O C K / D 3 /W I K I / G A L L E R Y

G E T S TA R T E D W I T H D 3

Page 5: Improving D3 Performance with CANVAS and other Hacks

B A S I C D 3 T U T O R I A L

• Adding nodes

• Mapping data to nodes

• Data Driven Documents

• Examples 01-06 at http://soasta.github.io/improving-d3-performance/d3/

Page 6: Improving D3 Performance with CANVAS and other Hacks

A N I M AT E D D 3 C H A R T S

• Force Directed Layout

• Reacting to user interaction

• Reacting to changing data

Page 7: Improving D3 Performance with CANVAS and other Hacks

F O R C E D I R E C T E D L AY O U TU S I N G P H Y S I C S

Page 8: Improving D3 Performance with CANVAS and other Hacks

F O R C E D I R E C T E D L AY O U T

• http://bl.ocks.org/mbostock/4062045

• Took basic Force Directed Layout and added enhancements:

• Convex hulls

• Labels

• Mouseovers

• Variable sized points

Page 9: Improving D3 Performance with CANVAS and other Hacks

F O R C E D I R E C T E D L AY O U T — P R O B L E M S

• Rendering is O(n) based on number of SVG nodes

• Calculations are O(n2) based on number of links

• Drawing the hull is expensive as nodes move around a lot

Page 10: Improving D3 Performance with CANVAS and other Hacks

F O R C E D I R E C T E D L AY O U T — S O L U T I O N S

• Reduce number of links by using a Minimum Spanning Tree

• Identify clusters and only link one node from each cluster

• Visually reduce nodes within the cluster using approximation

• Add decorators later as the animation stabilizes

Page 11: Improving D3 Performance with CANVAS and other Hacks

F O R C E D I R E C T E D L AY O U T — A D D I T I O N A L O P T I O N S

• We could use CANVAS to get rid of SVG nodes

• Create subgroups within each group to further reduce links

• Get rid of some of our visual enhancements

Page 12: Improving D3 Performance with CANVAS and other Hacks

E D G E B U N D L E D L AY O U TU S E R I N T E R A C T I O N

Page 13: Improving D3 Performance with CANVAS and other Hacks

E D G E B U N D L E D L AY O U T

• http://bl.ocks.org/mbostock/7607999

• We added many more links

• Links colored based on performance

• Links sized based on volume of data flow

• Mouseover should highlight connected links

• Nodes sized based on volume within that node

Page 14: Improving D3 Performance with CANVAS and other Hacks

E D G E B U N D L E D L AY O U T — P R O B L E M S

• Default behaviour is to iterate through every link on mouseover and change colour — this is slooooow!

• Using CSS class to change colour causes major re-render

• Quickly cycling through nodes has noticeable lag

Page 15: Improving D3 Performance with CANVAS and other Hacks

E D G E B U N D L E D L AY O U T — S O L U T I O N S

• First we tried to not iterate, just use CSS class cascades

• This was a trade off because we ended up adding a large number of classes, two per node and one per link

Page 16: Improving D3 Performance with CANVAS and other Hacks

E D G E B U N D L E D L AY O U T — S O L U T I O N S

• The second attempt was to use CANVAS to draw everything

• The problem here is that CANVAS is not great for text, and mouseovers no longer worked

Page 17: Improving D3 Performance with CANVAS and other Hacks

E D G E B U N D L E D L AY O U T — S O L U T I O N S

• The third attempt was to use CANVAS to draw links and SVG for nodes

• The biggest problem was to make sure they overlapped perfectly, ie, it was a small problem.

Page 18: Improving D3 Performance with CANVAS and other Hacks

C A N VA S D O E S N O T S U P P O R T B E Z I E R C U R V E S ! ! !

E X C E P T …

Page 19: Improving D3 Performance with CANVAS and other Hacks

D 3 W I T H C A N VA S

• There are some great examples online…

• https://bocoup.com/weblog/d3js-and-canvas

• https://gist.github.com/mbostock/1276463

• But not quite what we wanted

Page 20: Improving D3 Performance with CANVAS and other Hacks

D 3 W I T H C A N VA S

• Create a Custom XML NameSpace

• Map dummy nodes from this namespace for each link

• Each dummy node contains the SVG path required to draw the curve as well as other attributes

• After all nodes have been mapped, call a renderer to convert this to CANVAS

Page 21: Improving D3 Performance with CANVAS and other Hacks

d3.ns.prefix.custom = "https://foo-bar.com/edge_bundling.html";

d3.select("body").append("custom:sketch") .classed("link-container", true);

linkBinding.enter() .append("custom:path") .attr("lineCap", "round") .attr("lineJoin", "round") .attr("lineWidth", .2) .attr("opacity", 0.1) .attr("selected-opacity", 0.3);

linkBinding .attr("selected-lineWidth", function(d) { return weightScale(d.weight); }) .attr("d", function(d) { return line(d.path); });

C R E AT I N G N O D E S

Page 22: Improving D3 Performance with CANVAS and other Hacks

var pathAttrs = ["strokeStyle", "lineCap", "lineJoin", "lineWidth"];

ctx.beginPath();

pathAttrs.forEach(function(a) { var val = node.attr(selected + a) || node.attr(a); if(a === "strokeStyle") { var color = d3.rgb(val); val = "rgba(" + color.r + "," + color.g + "," + color.b + "," + node.attr(selected + "opacity") + ")"; } ctx[a] = val; });

A P P LY I N G S T Y L E S

Page 23: Improving D3 Performance with CANVAS and other Hacks

var path = node.attr("d"), m; while((m = path.match(/^ *([MLCQ])([\d.,e-]+) */))) { var cmd = m[1]; var coords = m[2].split(",").map(pathToCoords); path = path.replace(/^ *([MLCQ])([\d.,e-]+) */, "");

switch(cmd) { case "M": ctx.moveTo(coords[0], coords[1]); break; case "L": ctx.lineTo(coords[0], coords[1]); break; case "C": ctx.bezierCurveTo(coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); break; case "Q": ctx.quadraticCurveTo(coords[0], coords[1], coords[2], coords[3]); break; } }

D R A W I N G C U R V E S

Page 24: Improving D3 Performance with CANVAS and other Hacks

B U T I T S T I L L W A S N ’ T FA S T E N O U G H !

Page 25: Improving D3 Performance with CANVAS and other Hacks

E N H A N C E M E N T S

• Use 2 CANVAS elements,

• one to hold the background (ie, all links),

• the second to show the currently selected links

• Change opacity on the background to darken it

• If mouse pointer returns to last selected node, just redisplay

Page 26: Improving D3 Performance with CANVAS and other Hacks

S U M M A R Y

• Number of DOM nodes is very significant

• Reduce calculations on in-memory objects

• Show approximate data, based on available pixels and power

• Use CANVAS when mouse interaction is not required

• Cache repeated states rather than redrawing them

Page 27: Improving D3 Performance with CANVAS and other Hacks

Thank You