One of the main purposes of the Web is to present and exchange information. Information presentation can have many forms (text, graphics, audio, video, maps).

Graphically displaying information can be done in a static way or in a dynamic way.

  • Static graphics are basically images (pictures) or pre-generated charts.
  • Dynamic graphics occur when data, in a non graphical format (structured text, binary data), are transformed into graphics (charts).

These graphics can be generated on the server (e.g. a PHP script that generates a charts based on the results of a MySQL DB query). Or they can be generated on the client side with JavaScript, HTML, and CSS .

HTML5 has mainly 2 types technologies to display graphical data:

  • A pixel-based model for drawing:
    • in 2D with canvas
    • in 3D with WebGL
  • A DOM-based model that gives 2D elements DOM capabilities (events, positioning, styling, etc.): SVG

2D Drawing on the canvas

Canvas is primarily a DOM element that gives access to a 2D area for drawing.

A native API is available in JS: CanvasRenderingContext2D.

// <canvas id="my-canvas" width="200" height="200"></canvas>
var canvas = document.getElementById('my-canvas');
var context = canvas.getContext('2d');
context.fillStyle = "rgb(200,0,0)";
context.fillRect (10, 10, 100, 100);

Basic drawing capabilities :

  • various shapes (rectangles, lines, paths, arcs)
  • text
  • Fill and stroke styles
  • gradients & shadows
  • transformations (scale, translate, rotate)
  • images manipulation (at pixel level)

A basic example on CodePen

More advanced 2D drawing with animations, events, user interaction can be achieved with more coding. Usually Extra libraries are used:

3D Drawing with WebGL

WebGL is an API based on OpenGL ES. It uses the canvas DOM element to build 3D scenes.

Since OpenGL ES is a low level API, we mostly always use third party libraries:

SVG

SVG is an XML markup language for describing two-dimensional vector graphics.

<svg width="100" height="50" viewBox="0 0 3 2">
    <rect width="1" height="2" x="0" fill="#002395" />
    <rect width="1" height="2" x="1" fill="#ffffff" />
    <rect width="1" height="2" x="2" fill="#ED2939" />
</svg>

SVG Elements

Shape elements

<circle>, <ellipse>, <line>, <path>, <polygon>, <polyline>, <rect>

Structural elements

<defs>, <g>, <svg>, <symbol>, <use>

Text content elements

<altGlyph>, <textPath>, <text>, <tref>, <tspan>

SVG Attributes

Mostly for Animations and Events.

Writing SVG is not trivial. Once again we use libraries. May exist. The most achieved (and maybe the most difficult to learn) is D3.js

D3.js Data-Driven Documents

The general purpose of D3.js is to bind arbitrary data to a Document Object Model (DOM).

D3.js allow to create a graphical representation of a data model (e.g. an array of numbers becomes a pie chart, a set of coordinates becomes a map, etc.)

The Design Pattern

Classical steps to create data-linked DOM elements:

  1. Start from one existing DOM element
  2. Make a selection (empty) of DOM elements
  3. Link data to the selection
  4. Append new DOM elements to the selection (one Element per data item)
  5. modify the new DOM elements : 5.1. style 5.2. attributes 5.3. events
<svg width=200 height=200>
var user_data = [10, 20, 35, 12];
var svg = d3.select('svg'); // (1)
console.log(svg);
svg.selectAll('.block') // (2)
  .data(user_data) // (3)
  .enter() // data to be linked to DOM
  .append("circle") // (4)
  .classed("block", 1) // (5.1)
  .attr("cx", function(d, i) { // (5.2)
    return 100 * (1 + i);
  })
  .attr("cy", 100)
  .attr("r", function(d) {
    return d;
  })
  .on("click", function(d, i) { // (5.3)
    d3.select(this)
      .classed('clicked', 1);
  });

this example on CodePen

Mais on peut aussi mettre à jour et supprimer des éléments pour les synchroniser avec les données.

Idéalement on doit prévoir les 3 étapes création, mise à jour et suppression.

var text = g
  .selectAll("text")
  .data(data, key); // JOIN

text.exit() // EXIT
    .remove();

text.enter() // ENTER
  .append("text")
    .text(function(d) { return d; })
  .merge(text) // ENTER + UPDATE
    .attr("x", function(d, i) { return i * 32; });

pour aller plus loin :

Drawing paths

var p1 = 'M 0 0 L 0 10 L 10 10 L 20 10 L 20 20 L 30 20 L 30 30 L 30 40 ';
var p2 = 'M 10 20 L 20 20 L 20 30 L 10 30 L 10 20';

var paths = [{
  path: p1,
  fill: false
}, {
  path: p2,
  fill: true
}];

var svg = d3.select('svg');
console.log(svg);
svg.append("g")
  .attr("transform", "scale(10)")
  .selectAll('.block') // (2)
  .data(paths) // (3)
  .enter() // data to be linked to DOM
  .append("svg:path") // (4)
  .attr("d", function(d) { // (5.2)
    return d.path;
  })
  .classed('fill', function(d) {
    return d.fill
  })
  .classed('no-fill', function(d) {
    return !d.fill
  });

Path Example on CodePen

Handle events

var user_data = [ 10, 20, 35, 22, 25, 40];

var coordiate_width = 800;
var display_width = 400;

var svg = d3.select('#svg1')
  .append("svg")
  .attr("width", display_width)
  .attr("height", display_width/2)
  .append("g")
  .attr("transform", "scale("
    +(display_width/coordiate_width)+")");

var blocks = svg.selectAll('.block')
  .data(user_data)
  .enter()
  .append("circle")
  .classed("block",true)
  .attr("cx", function(d, i) {
      return 100*(1+i);
  })
  .attr("cy", coordiate_width/4)
  .attr("r", function(d, i) {
      return d;
  })
  .on("mouseover", function(d, i){
    d3.select(this)
      .classed('over', 1);
  })
  .on("mouseout", function(d, i){
    d3.select(this)
      .classed('over', 0);
  });
.block {
  stroke: #55F;
  stroke-width: 2px;
  stroke-linecap: square;
  fill: #EEE;
}

.block.over {
  stroke: #f99;
  stroke-width: 3px;
  fill: #DDD;
}

Events Example

Fonctionnement avec React et Redux

Dans une application avec React et Redux, la source unique d’information est le store redux.

On va créer des composants react connectés au store et répondre aux évènements de mise à jour du composant réact (le cycle de vie du composant):

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount