But nothing new
Navigate from one page to the other via hyperlinks
Vectorial vs. Bitmap
Retained mode vs. Immediate mode
Retained mode (SVG) | Immediate mode (Canvas) |
---|---|
Complete model of the graphical objects | Draw and forget |
Rendering managed by the engine | Full control over rendering |
Handlers can be attached to graphical elements | Interactions from mouse coordinates |
Good performance with a reasonable number of objects | “Constant” performance |
14 October 2013
name | age | height | weight |
---|---|---|---|
Alice | 4 | 100 | 17 |
Bruno | 2 | 88 | 12 |
Charles | 85 | 177 | 57 |
Dédé | 25 | 168 | 67 |
Etienne | 42 | 182 | 80 |
Françoise | 8 | 127 | 25 |
Gégé | 50 | 160 | 83 |
<svg width="640" height="480">
<circle cx="24" cy="280" r="17" opacity=".5"/>
<circle cx="12" cy="304" r="12" opacity=".5"/>
<circle cx="510" cy="126" r="57" opacity=".5"/>
<circle cx="150" cy="144" r="67" opacity=".5"/>
<circle cx="252" cy="116" r="80" opacity=".5"/>
<circle cx="48" cy="226" r="25" opacity=".5"/>
<circle cx="300" cy="160" r="83" opacity=".5"/>
</svg>
Inverted y-axis (origin at top left)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<div id="placeholder"></div>
<script src="d3.js"></script>
<script src="main.js"></script>
</body>
</html>
Available at
// main.js
// TODO
[
{"name": "Alice", "age": 4, "height": 100, "weight": 17},
{"name": "Bruno", "age": 2, "height": 88, "weight": 12},
{"name": "Charles", "age": 85, "height": 177, "weight": 57},
{"name": "Dédé", "age": 25, "height": 168, "weight": 67},
{"name": "Etienne", "age": 42, "height": 182, "weight": 80},
{"name": "Françoise", "age": 8, "height": 127, "weight": 25},
{"name": "Gégé", "age": 50, "height": 160, "weight": 83}
]
Native format
name,age,height,weight
Alice,4,100,17
Bruno,2,88,12
Charles,85,177,57
Dédé,25,168,67
Etienne,42,182,80
Françoise,8,127,25
Gégé,50,160,83
More compact than JSON
d3.json('path/to/file.json', function(error, dataset) {
if (error) return console.warn(error);
// TODO: Visualize the dataset
});
d3.csv('path/to/file.csv', function(error, dataset) {
if (error) return console.warn(error);
// TODO: Visualize the dataset
});
var visualize = function(error, dataset) {
if (error) return console.warn(error);
// TODO: Visualize the dataset
};
d3.csv('path/to/file.csv', visualize);
d3.select('body');
d3.select('.class');
d3.select('#id');
d3.select('[color=black]');
d3.select('parent child');
d3.select('.this.that');
d3.select('.this, .that');
var placeholder = d3.select('#placeholder');
Include Sizzle if needed
d3.selectAll('body');
d3.selectAll('.class');
d3.selectAll('#id'); // FIXME: Non-unique IDs?
d3.selectAll('[color=black]');
d3.selectAll('parent child');
d3.selectAll('.this.that');
d3.selectAll('.this, .that');
var placeholders = d3.selectAll('.placeholder');
Include Sizzle if needed
var imgs = d3.selectAll('img')
.attr('title', 'Tooltips for everyone!');
var h2s = d3.selectAll('h2')
.style('font-style', 'italic')
.style('font-weight', 'bold');
var ps = d3.selectAll('p')
.text('pwned');
var pz = d3.selectAll('p')
.html('<blink>pwned</blink>');
var svg = d3.select('#placeholder')
.append('svg')
.attr('width', 640)
.attr('height', 480);
Fluent interface (method chaining)
Strings = no limitations linked to the library
append
and insert
return a new selection
var svg = d3.select('#placeholder')
.append('svg')
.attr('width', 640)
.attr('height', 480);
var visualize = function(error, dataset) {
if (error) return console.warn(error);
// TODO: Visualize the dataset
};
d3.csv('path/to/file.csv', visualize);
var visualize = function(error, dataset) {
if (error) return console.warn(error);
for (var i = 0; i < dataset.length; i++) {
var person = dataset[i];
svg.append('circle')
.attr('opacity', 0.5)
.attr('r', person.weight)
.attr('cx', person.age * 6) // 100 years old * 6 ~= 640
.attr('cy', 480 - person.height * 2); // 200 cm high * 2 ~= 480
}
};
The End?
name | age | height | weight |
---|---|---|---|
Alice | 4 | 100 | 17 |
Bruno | 2 | 88 | 12 |
Charles | 85 | 177 | 57 |
Dédé | 25 | 168 | 67 |
Etienne | 42 | 182 | 80 |
Françoise | 8 | 127 | 25 |
Gégé | 50 | 160 | 83 |
name | age | height | weight |
---|---|---|---|
Alice | 9 | 140 | 30 |
Bruno | 7 | 110 | 22 |
Dédé | 30 | 172 | 75 |
Etienne | 47 | 182 | 70 |
Zoé | 2 | 84 | 10 |
Françoise | 13 | 141 | 42 |
var visualize = function(error, dataset) {
if (error) return console.warn(error);
svg.selectAll('circle').remove();
for (var i = 0; i < dataset.length; i++) {
var person = dataset[i];
svg.append('circle')
// ...
}
};
d3.csv('path/to/dataset-2000.csv', visualize);
// Called later...
d3.csv('path/to/dataset-2005.csv', visualize);
Selection of a selection d3.select('svg').selectAll('circle')
No animation... no fun!
aka Join
var visualize = function(error, dataset) {
if (error) return console.warn(error);
var circles = svg
.selectAll('circle')
// The unique key is the name.
.data(dataset, function(d) { return d.name; });
// UPDATE
circles
//...
// ENTER
circles
.enter()
//...
// EXIT
circles
.exit()
//...
};
// UPDATE
circles
.transition().duration(750)
.call(positionAndSizeCircle);
// ENTER
circles
.enter()
.append('circle')
.style('opacity', 0)
.call(positionAndSizeCircle)
.transition().duration(750)
.style('opacity', 0.5);
// EXIT
circles
.exit()
.transition().duration(750)
.style('opacity', 0)
.remove();
var positionAndSizeCircle = function(circle) {
circle
.attr('r', function(d) {
return d.weight;
})
.attr('cx', function(d) {
return d.age * 6; // 100 years old * 6 ~= 640
})
.attr('cy', function(d) {
return 480 - d.height * 2; // 200 cm high * 2 ~= 480
});
};
Sticky data: __data__
This presentation has been created with
some code
some fonts
some pixels
Slides: ptamarit.com/slides-data-viz-web-d3
Sources: github.com/ptamarit/slides-data-viz-web-d3
Pablo Tamarit · @ptamarit