Graphics – Time for a change – Evolution



Graphics – Time for a change – Evolution

0 2


paths-talk-slides

Slides for the talk on Paths.js

On Github andreaferretti / paths-talk-slides

Graphics

Time for a change

Presentation at Milano JS User group

Evolution

Application in the browser are starting to become

  • more structured
  • more declarative

Evolution

This is what we used to do

$(function() {
  var list = $('#playground').append('ul');

  cities.forEach(function(city) {
    list.append('li')
      .data('city', city)
      .append('a')
      .text(city)
      .click(showIt);
  });
});

Evolution

...and also

function showIt(event) {
  var city = $(event.target).data('city');
  alert('You clicked on ' + city);
  event.preventDefault();
}

Evolution

This can become more declarative

<div id="playground">
  <ul>
    <li ng-repeat="city in cities">
      <a ng-click="print(city)">{{ city }}</a>
    </li>
  </ul>
</div>
$scope.print = function(city) {
  alert('You clicked on ' + city);
};

This has not happened for graphics

Visualization

Today we can do a lot in the browser

Visualization

Today we can do a lot in the browser

Visualization

Today we can do a lot in the browser

What has allowed this?

HTML 5!!!

We can embed SVG inside HTML

SVG tags

<svg width="800" height="500">

SVG tags

<svg width="800" height="500">
  <rect width="200" height="200" />
</svg>

SVG tags

<svg width="800" height="500">
  <rect width="200" height="200" />
  <circle cx="250" cy="250" r="40" />
</svg>

SVG tags

<svg width="800" height="500">
  <g>
    <rect width="200" height="200" />
    <circle cx="250" cy="250" r="40" />
  </g>
</svg>

SVG tags

<svg width="800" height="500">
  <g>
    <rect width="200" height="200" />
    <circle cx="250" cy="250" r="40" />
  </g>
  <line x1="300" y1="300" x2="200" y2="600" />
</svg>

SVG tags

<svg width="800" height="500">
  <g>
    <rect width="200" height="200" />
    <circle cx="250" cy="250" r="40" />
  </g>
  <line x1="300" y1="300" x2="200" y2="600" />
  <text x="0" y="15">But the most general tag is...</text>
</svg>

SVG tags

<svg width="800" height="500">
  <g>
    <rect width="200" height="200" />
    <circle cx="250" cy="250" r="40" />
  </g>
  <line x1="300" y1="300" x2="200" y2="600" />
<path d="M 110 115 A 140 140 0 0 1 57 297 L 24 255 A 60 60 0 0 0 48 163 Z"/>
</svg>

What?!?!?

SVG tags

Transforms

First, create a simple graphics, then adjust it

<svg width="800" height="500">
  <g transform="rotate(90)">
    <rect width="200" height="200" />
    <circle cx="50" cy="50" r="40" />
  </g>
  <g transform="scale(1.2) translate(100, 100)">
    <rect width="200" height="200" />
    <circle cx="50" cy="50" r="40" />
  </g>
</svg>

Path operations

What was that nonsense in path?

  • M = moveto
  • L = lineto
  • H = horizontal lineto
  • V = vertical lineto
  • C = curveto
  • S = smooth curveto
  • Q = quadratic Bézier curve
  • T = smooth quadratic Bézier curveto
  • A = elliptical Arc
  • Z = closepath

Path operations

What was that nonsense in path?

  • M x y
  • L x y
  • H x
  • V y
  • C x1 y1 x2 y2 x y
  • S x2 y2 x y2
  • Q x1 y1 x y
  • T x y
  • A rx ry xrot large_arc_flag sweep_flag x y
  • Z

Lowercase letters for relative coordinates

The current state of graphics in the browser

  • Ad hoc libraries (Highcharts, Flot, ...)
  • Customizable toolboxes (D3, ?)

Most graphics libraries are not customizable enough in:

  • Appearance
  • Animation
  • Interaction

The D3 approach

D3 works by pairing data and elements

The D3 approach

D3 works by pairing data and DOM elements

We can first select existing DOM elements and attach data to them...

The D3 approach

D3 works by pairing data and DOM elements

We can first select existing DOM elements and attach data to them...

...or select existing data and generate associated DOM

Example 1

d3.select("div.output svg")
  .selectAll("rect") // Three existing rectangles
  .data([127, 61, 256])
  .attr("x", 0)
  .attr("y", function(d,i) { return i*90+50 })
  .attr("width", function(d,i) { return d; }) // Function of the datum
  .attr("height", 20)
  .style("fill", "steelblue")
          

Example 2

d3.select("div.output svg")
  .selectAll("rect") // No existing rectangles by now
  .data([127, 61, 256])
  .enter() // what's this?
  .append("rect")
  .attr("x", 0)
  .attr("y", function(d,i) { return i*90+50 })
  .attr("width", function(d,i) { return d; })
  .attr("height", 20)
  .style("fill", "steelblue")
          

Stage metaphor

var selection = d3.select("rect").data([...]);

Stage metaphor

var selection = d3.select("rect").data([...]);

The enter selection matches data not yet bound to elements

var enter = selection.enter();
enter.append("rect") // Now it has elements
            

Stage metaphor

var selection = d3.select("rect").data([...]);

The enter selection matches data not yet bound to elements

var enter = selection.enter();
enter.append("rect") // Now it has elements
            

The exit selection matches elements not yet bound to data

var exit = selection.exit();
exit.remove() // No more mismatch
            

The rest looks pretty much like jQuery

selection
  .transition()
  .duration(3000)
    .attr("x", 0)
    .attr("y", function(d,i) { return i*90+50 })
    .attr("width", function(d,i) { return d; })
    .attr("height", 20)
    .style("fill", "steelblue")
    .transition()
    .duration(3000)
    .delay(3000)
      .style("fill", "green")
      .attr("width", function(d,i) {
          return d*1.5;
        })
          

Stage pattern

  var selection = parent.select('.foo').data(foos);

  selection.enter().append('rect').attr('class', 'foo');

  selection.attr('width', function(d) { return d.width; });

  selection.exit().remove();
            

Stage pattern

  var selection = parent.select('.foo').data(foos);

  selection.enter().append('rect').attr('class', 'fop');
  // What happens here?

  selection.attr('width', function(d) { return d.width; });

  selection.exit().remove();
            

D3 is very powerful and versatile, but..

D3 is very powerful and versatile, but..

  • it is as imperative as jQuery

D3 is very powerful and versatile, but..

  • it is as imperative as jQuery
  • it is hard to imagine the final result

D3 is very powerful and versatile, but..

  • it is as imperative as jQuery
  • it is hard to imagine the final result
  • pretends to have full control over the DOM

D3 is very powerful and versatile, but..

  • it is as imperative as jQuery
  • it is hard to imagine the final result
  • pretends to have full control over the DOM
  • ⇒ hard to integrate with MVC frameworks

D3 is very powerful and versatile, but..

  • it is as imperative as jQuery
  • it is hard to imagine the final result
  • pretends to have full control over the DOM
  • ⇒ hard to integrate with MVC frameworks
  • the stage metaphor is just weird to work with

D3 is very powerful and versatile, but..

  • it is as imperative as jQuery
  • it is hard to imagine the final result
  • pretends to have full control over the DOM
  • ⇒ hard to integrate with MVC frameworks
  • the stage metaphor is just weird to work with
  • includes everything and the kitchen sink (Ajax, CSV parsing, formatting, localization, colors, time...)

Idea 㦕!

What if we left all the rendering to frameworks like React, Angular, Ractive?

Idea 㦕!

What if we left all the rendering to frameworks like React, Angular, Ractive?

Data binding could be used to make animations!

Pros

  • Easy to visualize
  • Easy to style
  • Easy to integrate

We just need a way to derive geometric shapes from the data

Paths.js

The path as a low level component

var path = Path() // paths are immutable
  .moveto(10, 20) // hence each operation returns a new path
  .lineto(30, 50)
  .lineto(25, 28)
  .qcurveto(27, 30, 32, 27)
  .closepath();
var d = path.print(); // The string representation
          
// in the template...
<path d="{{ d }}" fill="red" />
          

Shapes

Paths for common shapes can be generated from geometric data

var rectangle = Rectangle({top: 10, bottom: 3, left: -2, right: 5 });
var path = rectangle.path;
var labelPosition = rectangle.centroid; //useful to place labels
var sector = Sector({
  center: [10, 20],
  r: 5,
  R: 15,
  start: 0,
  end: Math.PI / 2
});
var path2 = sector.path;
          

Graphs

Collection of shapes can be derived from data

  var pie = Pie({
    data: [
      { name: 'Italy', population: 59859996 },
      ...
    ],
    accessor: function(x) { return x.population; }, // how to read the datum
    center: [20, 15],
    r: 30,
    R: 50
  });
            

Graphs

The template side

<svg>
{% for curve in pie.curves %}
  <path d="{ curve.sector.path.print() }" fill="{ color(curve.index) }" >
{% endfor %}
<svg>
            

Why should I care?

  • Other graphs: bar charts, waterfall, radar charts, trees...

Why should I care?

  • Other graphs: bar charts, waterfall, radar charts, trees...
  • Can still generate your own going lower level

Why should I care?

  • Other graphs: bar charts, waterfall, radar charts, trees...
  • Can still generate your own going lower level
  • Evertyhing is an AMD module: include only what you need...

Why should I care?

  • Other graphs: bar charts, waterfall, radar charts, trees...
  • Can still generate your own going lower level
  • Evertyhing is an AMD module: include only what you need...
  • ...but still small if you put everything

Why should I care?

  • Other graphs: bar charts, waterfall, radar charts, trees...
  • Can still generate your own going lower level
  • Evertyhing is an AMD module: include only what you need...
  • ...but still small if you put everything
  • Only pure functions: easier to test

Why should I care?

  • Other graphs: bar charts, waterfall, radar charts, trees...
  • Can still generate your own going lower level
  • Evertyhing is an AMD module: include only what you need...
  • ...but still small if you put everything
  • Only pure functions: easier to test
  • Only pure functions: works on Node as well!

Time for live examples!

But first, some links

  • http://www.w3.org/TR/SVG/
  • http://d3js.org/
  • https://github.com/andreaferretti/paths-js
  • http://andreaferretti.github.io/paths-js-demo/
  • http://mlarocca.github.io/01-22-2014/pathsjs_ractive.html

THE END

By Andrea Ferretti