Updated on
Dynamic Graphical Data
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 of 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)
More advanced 2D drawing with animations, events, user interaction can be achieved with more coding. Usually Extra libraries are used:
- general purpose 2D:
- 2D game engines:
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 use third party libraries:
- http://threejs.org/
- http://www.pixijs.com/ for 2D with WebGL
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:
- Start from one existing DOM element
- Make a selection (empty) of DOM elements
- Link data to the selection
- Append new DOM elements to the selection (one Element per data item)
- modify the new DOM elements :
- style
- attributes
- 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;
});
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); // 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; });
Lire la suite sur le site de d3 :
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
});
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;
}
Fonctionnement avec React
Utiliser D3 avec React n’est pas trivial. Les 2 bibliothèques font plus ou moins la même chose (manipuler des données ainsi que le DOM). Il y a plusieurs approches possibles. La plus simple est de laisser D3 gérer le DOM et de laisser React gérer les données.
On utilise le hook useRef
pour créer une référence à un élément div que nous utiliserons pour rendre le graphique. Ensuite, dans le hook useEffect
, on utilise D3
pour créer un élément svg et l’ajouter à la div. Nous utilisons ensuite D3 pour lier les données aux éléments svg et rendre le graphique.