I am Roberto.
And it was mainly advertisement, gaming, campaigns. A lot of fun
Not so fun
D3.js is a set of tools, in JavaScript for manipulating documents based on data.
D3 comes from Data Driven Documents
3 D :)
D3 helps you bring data to life using HTML, SVG and CSS. and Canvas.
SVG are described by functions
size agnostic where raster ( jpeg ) are size dependant
SVG are one of the best solution for responsive design
HTML Canvas is a tag, first introduced by Apple, in 2004
the Canvas tag heps to draw graphics, on the fly, via scripting (usually JavaScript).
(data manipulation, binding, render)
It is not opinionated (compared to highcharts etc)
From v4 is modular, so you can use just what you need
It is lightweight compared to plotly (220kb vs 2.5Mb)
It is well documented and great community
Ah, forgot. it is open source
d3 espouses abstractions that are useful for any visualization application and rejects the tyranny of charts.
What is D3
How to use D3
What can you do with D3
Why use
Data manipulation
Data Binding
Data Rendering
let svg = d3.select("#d3-container").append("svg")
.append("circle")
.attr("cx", (width * .5) / 2 )
.attr("cy", height * .5)
.attr("r", 200)
.style("fill", "steelblue");
const mydata = [5, 3, 2, 8];
svg.selectAll('circle')
.data(mydata)
.enter()
.append("circle")
.attr("cx", (width * .5) / 2)
.attr("cy", (d, i) => {
return i * 100 + 100
})
.attr("r", (d) => {
return 10 * d;
})
.style("fill", "yellow");
Before you can change or modify any elements in d3 you must first select them.
you can use d3.select() or d3.selectAll()
An array will be returnedBrain use more energy if things just appear on screen, without a natural explanation
let t = d3.transition()
.ease(d3.easeCubic)
.duration(1500);
...
** bind data
.data(mydata)
.enter()
.append("circle")
...
.attr("r", (d) => {
return 0 * d;
})
.transition(t)
.attr("r", (d) => {
return 10 * d;
})
// random array of numbers.
// random array of colors
const mydata = [25, 25, 40, 10];
const mycolor = ['yellow', 'red', 'green', 'black'];
// we bind data
svg.selectAll('rect')
.data(mydata)
.enter()
.append("rect")
.attr("height", 0)
.style("fill", (d, i) => {
return mycolor[i];
})
.transition(t)
.attr("height", (d, i) => {
let perc = (mydata[i] * 1) / sum;
return height * perc;
});
.attr("y", (d, i) => {
let returnY = 0;
let percTotal = 0;
for (let j = i - 1; j > -1; j--) {
percTotal += (mydata[j]) / sum;
}
returnY = height * percTotal;
return returnY
})
.attr("width", (d) => {
return width;
})
.transition()
.delay((d, i) => {
return 300 * i;
})
.duration(1000)
.attr("width", (d) => {
return width;
})
svg
.append("text")
.attr('id', 'id-used-to-retrieve-the-text')
.text('CIAO BELLA')
.style('fill', 'black')
.style('opacity', 1);
.on('mouseover', (d, i, nodes) => {
let rect = nodes[i].getBoundingClientRect();
let text = d3.select(nodes[i])
.append("text")
.attr('id', 't' + i)
.text(d)
.style('opacity', 1);
})
.on('mouseout', (d, i, nodes) => {
d3.select('#t' + i).remove();
})
const mycolor = ['yellow', 'red', 'green', 'pink', '#ff0099', '#ffff99', '#DDCCFF', '#c9fa05'];
setInterval(() => {
let mydata = [];
for(let i = 0; i < 10; i++) {
mydata.push(3 + Math.floor(Math.random() * 40));
}
renderChart(mydata);
}, 2000);
let rects = svg.selectAll('rect')
.data(mydata)
//**REMOVE
//** UPDATE
//**ADD
let rects = svg.selectAll('rect')
.data(mydata)
//**REMOVE
rects
.exit()
.remove()
//** UPDATE
//** ADD
let rects = svg.selectAll('rect')
.data(mydata)
//**REMOVE
...
//** UPDATE
rects
.attr("x", 0)
.attr("y", (d, i) => {
let returnY = 0;
let percTotal = 0;
for (let j = i - 1; j > -1; j--) {
percTotal += (mydata[j]) / sum;
}
returnY = height * percTotal;
return returnY
})
.attr("width", (d) => {
return width;
})
.attr("height", (d, i) => {
let perc = (mydata[i] * 1) / sum;
console.log('update ?perc ', perc);
return height * perc;
})
//**ADD
...
let rects = svg.selectAll('rect')
.data(mydata)
//**REMOVE
...
//** UPDATE
...
//**ADD
rects
.enter()
.append("rect")
.attr("x", 0)
.attr("y", (d, i) => {
let returnY = (calc y based on perc)...
return returnY
})
.attr("height", (d, i) => {
let perc = (mydata[i] * 1) / sum;
console.log('add ?perc ', perc);
return height * perc;
})
.attr("width", (d) => {
return width;
})
}
Basic rendering in d3, and binding data
animate data
d3 data model: add, update, remove data
“Scales are functions that map from an input domain to an output range.”
We think about dimensions as spatial and quantitative
But when working with abstract data
there are ordinal data or categorical
Let's consider fish for example.
The order could be:
by weight (quantitative)
by quality (good very good)
or categorical (type of fish)
So scales are functions that take abstract values and convert to a visual value (like x, y position)
let x = d3.scaleLinear()
.domain([100, 500])
.range([0, 960]);
console.log(x(100));//0
console.log(x(250)); // 360
We declare the type of scale (linear in this case)
We set the input (Domain)
We set the the output (range)
const mydata = [15, 3, 2, 8];
let xScale = d3.scaleLinear()
.domain([d3.min(mydata), d3.max(mydata)])
.range([0 + 100, height - 100]);
...
.attr("cy", (d, i) => {
return xScale(d)
})
let scale = d3.scaleLinear()
.domain([d3.min(mydata), d3.max(mydata)])
.range([0, Math.PI*2]);
svg.selectAll('circle')
.data(mydata)
.enter()
.append("circle")
.attr("cx", (d, i) => {
return Math.cos(scale(i)) * r + c.x;
})
.attr("cy", (d, i) => {
return Math.sin(scale(i)) * r + c.y;
})
let color = d3.scaleLinear()
.domain([10, 100])
.range(["yellow", "pink"]);
We will scale colors from yellow to pink
let scaleColor = d3.scaleLinear()
.domain([d3.min(mydata), d3.max(mydata)])
.range(['yellow', 'red']);
...
.style("fill", (d) => {
return scaleColor(d);
})
const data = [
{key: "porsche", value: 132},
{key: "ferrari", value: 71},
{key: "lambo", value: 337},
{key: "lotus", value: 93},
{key: "alfaromeo", value: 78}
];
let xScale = d3.scaleBand()
.domain(data.map(function(d){
return d.key;
}))
.range([0, 200]);
let yScale = d3.scaleLinear()
.domain([
d3.min(data, (d) => {
return d.value
}),
d3.max(data, (d) => {
return d.value
})
])
.range([0, 500]);
console.log('xScale ', xScale('ferrari'));
console.log('yScale ', yScale(337));
let xScale = d3.scaleBand()
.domain(data.map(function(d){
return d.key;
}))
.range([0, width / 2])
.padding([0.1]);
So, here we create the scale for x ( scaleBand)
let yScale = d3.scaleLinear()
.domain([
d3.min(data, (d) => {
return d.value
}),
d3.max(data, (d) => {
return d.value
})
])
.range([0, height - 100]);
let scaleColor = d3.scaleLinear()
.domain([
d3.min(data, (d) => {
return d.value
}),
d3.max(data, (d) => {
return d.value
})
])
.range(['green', 'red']);
one of the most powerful things, in d3, is the tools to group and nest data
const data = [
{
"car": "Porsche",
"model": "P1",
"maxSpeed": "300",
"color": "black",
},
{
"car": "Ferrari",
"model": "testa-rossa",
"maxSpeed": "320",
"color": "red",
},
{
"car": "Ferrari",
"model": "testa-nera",
"maxSpeed": "320",
"color": "red",
},
{
"car": "Fiat",
"model": "Uno",
"maxSpeed": "120",
"color": "white",
},
{
"car": "Fiat",
"model": "Uno-turbo",
"maxSpeed": "122",
"color": "red",
},
{
"car": "Fiat",
"model": "Uno-turbo",
"maxSpeed": "125",
"color": "black",
},
{
"car": "Porsche",
"model": "PP",
"maxSpeed": "290",
"color": "black",
}
]
let parsedResults = d3.nest()
.key(function(d) { return d.car ; })
.entries(dataNesting);
So, this is great for visualize data
But we can do more
let parsedResults = d3.nest()
.key((d) => {
return d[value] ;
})
.rollup((v) => {
return v.length;
})
.entries(dataNesting);
Scales (for colors, for positions)
Bar chart example
Array manipulation (nest, rolling)
Your framerate per second will go close to zero.
canvas / WebGL, (that run on canvas)
WebGL enables web content to use an API based on OpenGL ES 2.0 to perform 3D rendering in an HTML, using Canvas
d3.selectAll('circle')
.data(mydata)
.enter()
.append('custom')
.each((d,i) => {
let semicircle = new PIXI.Graphics();
...
});
if (times) {
intervalId = setInterval(() => {
let dd = [];
for (let i = 0; i < 10; i++) {
let rn = Math.floor(Math.random() * 50 + 10);
dd.push(rn);
}
executeSimpleRender(dd, 1);
}, 500);
}
d3.selectAll('circle')
.data(data)
.enter()
.append('custom')
.each(function(d,i) {
... pixi render ...
}