On Github Yonet / sfhtml5-d3-talk
Slides : bit.ly/sfhtml5-d3
Aysegul Yonet / @AysegulYonet
<circle cx="250" cy="100" r="30" fill="#25B0B0"> </circle>Let's take a look at circle. Notice we are calculating an area of a circle each time we draw a circle using these attributes. That means we have to be aware of the performance issues.
<rect x="200" y="50" width="100" height="100" rx="10" ry="0" fill="#25B0B0"> </rect>Interesting fact: if a properly specified value is provided for rx but not for ry (or the opposite), then the browser will consider the missing value equal to the defined one.
<g transform="translate(100, 100)"> <circle cx="20" cy="10" r="30" fill="rgb(255, 0, 0)"> </circle> <circle cx="100" cy="10" r="30" fill="#25B0B0"> </circle> </g>Any transformations (positioning, scaling, rotating) that are applied to the group element will also be applied to each of the child elements.
<line x1="10" y1="10" x2="100" y2="100" stroke="blue" stroke-width="100"> </line>
<path d=" M50,100 L131,66 L259,115 L339,50 L400,98 M350,150 L100,150" fill="none" stroke="#25B0B0"> </path>
<svg> <text x="100" y="100" fill="red" font-family="arial" font-size="16"> Look at me, I am a text. </text> </svg>Unless you set the style of the SVG text, it inherits font-styles from CSS.
var paragraphs = $("div p");
var paragraphs = d3.select("div").selectAll("p");jsFiddle D3 uses css selectors to define a selection. The selection can be based on tag, class, identifier, attribute, or place in the hierarchy. Child nodes might not exist at the time of our selection. One thing to note, D3 selector object only contains the elements that matched the selection rule when the selection was first created.
var p = d3.select("body").append('p'); p.html('Hello SFHTML5!').attr('style', 'color: red;');Once we have a selection, we can operate on our nodes.
d3.selectAll("p") .data([0, 1, 1, 2, 3, 5, 8, 13, 21, 34])Most importantly we can bind data to our selections. When data is bound to a selection, each element in the data array is paired with the corresponding DOM node in the selection. We can use the data to define the attributes on that node
d3.select("body").selectAll("p") .data([3, 7, 21, 31, 35, 42]) .enter();If there are fewer nodes than data, the extra data elements form the enter selection, which you can instantiate by appending to the enter selection.
d3.select("body").selectAll("p") .data([3, 7, 21, 31, 35, 42]) .enter().append("p") .text(function(d) { return "I’m number " + d + "!"; });D3 methods returns the selection they act upon, and we can apply multiple operations to the same selection. Here we are able to add text to our newly appended selection.
var bars = d3.select('body').selectAll('p').data(newData); //if we have more data, add new 'p' for those data items bars.enter().append('p') //if we have less data points, remove the 'p' that no longer have a data pairing. bars.exit().remove();What if we have less data points? Using D3’s exit and remove, you can get rid of outgoing nodes that are no longer needed. Like the enter method, exit selects the extra nodes that doesn't have data matching and remove deletes the nodes from our DOM.
// Update… var p = d3.select("body").selectAll("p") .data([3, 7, 21, 31, 35, 42]); // Enter… p.enter().append("p") // Exit… p.exit().remove();This is what we call the update pattern. If you forget about the enter and exit selections, you will automatically select only the elements for which there is corresponding data. By handling these three cases separately, you specify precisely which operations run on which nodes.
d3.select("body").selectAll("p") .data([3, 7, 21, 31, 35, 42]) .enter().append("p") .exit().remove();If you have been to the d3's website, I am sure you came accross the chained methods. Every chained method operates on the previous selection, and most operations return the same selection. Some methods return a new one! For example, selection.append returns a new selection containing the new elements. This conveniently allows you to chain operations into the new elements. Since method chaining can only be used to descend into the document hierarchy, use var to keep references to selections and go back up.
circles.attr("r", "0") .attr({ 'cx': function(d) {return (d * 2); }, 'cy': 10 }) .transition() .duration(750) .delay(function(d, i) { return i * 100; }) .attr("r", 10);Transition will happen between what you have assigned before and after the transion is called.
var scale = d3.scale.linear();Examples we saw so far calculates the position of our SVG elements by using data. D3 comes with scale methods to make these mappings easy. Scales transform a dataset in a certain interval (called the domain) into a number in another interval (called the range). For ex, transforms a domain in miles to a range in pixels.
var xScale = d3.scale.linear() .domain([0, d3.max(data)])// your data minimum and maximum .range([0, 420]);//the pixels to map to d3.select(".chart") .selectAll("div") .data([3, 7, 21, 31, 35, 42]) .enter().append("div") .style("width", function(d) { return xScale(d) + "px"; })JSFiddle Scale func creates a new instance of a scale. On our new scale, we can create new properties that persists.
var xScale = d3.scale.linear() .domain([0, 1000]) .range([0, 200]); console.log(xScale(500)); //100 console.log(xScale.domain());//[0, 1000]Domain and range is a setter and getter. If you don't pass any arguments it is returning the values.
Ordinal Scale have a discrete domain, such as a set of names or categories.
var xScale = d3.scale.ordinal() .domain(["Bob", "Stuart", "Kevin", "Scarlet"]) .rangePoints([0, 100]); console.log(xScale("Stuart"));//33.333333333333336 xScale.range(); // [0, 33.333333333333336, 66.66666666666667, 100]You need to have the same number of arguments. If you have more in range, that is OK. RangePoints subdivides everything in between the values you give to it.
d3.scale.category10()
Constructs a new ordinal scale with a range of ten categorical colors:var svg = d3.select('body').append('svg') .attr({ 'width': 250, 'height': 250 }); var xScale = d3.scale.linear() .domain([0, 5]) .range([0, 200]);
var render = function(data, r, color){ //Bind data var circles = svg.selectAll('circle').data(data); //Enter and update circles.enter().append('circle') .attr({ 'cx': function(d){return xScale(d)}, 'cy': 30, 'r': r, 'fill': function(){return color || 'red'} }); }
var oldData = [1, 2, 3]; var newData = [1, 2, 3, 4, 5]; render(oldData, 10); render(newData, 15, 'blue');How many people think the circles all should be blue?
var render = function(data, r, color){ //Bind Data var circles = svg.selectAll('circle').data(data); //Enter circles.enter().append('circle') //Update circles.attr({ 'cx': function(d){return xScale(d)}, 'cy': 30, 'r': r, 'fill': function(){return color || 'red'} }) }I am using a function to return a default value if the color is not defined.
var oldData = [1, 2, 3, 4, 5]; var newData = [1, 2, 3]; render(oldData, 10); render(newData, 15, 'blue');
var render = function(data, r, color){ //Bind Data var circles = svg.selectAll('circle').data(data); //Enter circles.enter().append('circle') //Update circles.attr({ 'cx': function(d){return xScale(d)}, 'cy': 30, 'r': r, 'fill': function(){return color || 'red'} }) //Remove circles.exit().remove(); }
day,donut Monday,91 Tuesday,23 Wednesday,7 Thursday,4 Friday,82 Saturday,39 Sunday,27
var x = "day"; var y = "donut"; var accessor = function(d) { d[y] = parseInt(d[y]); return d; } d3.csv("data/data.csv", accessor, function(d) { console.log(d);//Array of objects [{day: "Monday", donut: 91},...] render(d); });
[0, 1, 2, 3].reduce(function(a, b) { return a - b; });//-6 [0, 1, 2, 3].reduceRight(function(a, b) { return a - b; });//0left-to-right
var donutData = [ {day: 'Monday', donut: 34, variety: "plain"}, {day: 'Tuesday', donut: 41, variety: "glazed"} ];
var nestedData = d3.nest() .key(function(d){ return d.day;}) .entries(donutData); [{ key: "Monday", values: [ {day: 'Monday', donut: 34, variety: "plain"} ]}, { key: "Tuesday", values: [ {day: 'Tuesday', donut: 41, variety: "glazed"} ]} ]
var nestedData = d3.nest() .key(function(d){ return d.day;}) .key(function(d){ return d.variety;}) .entries(donutData); [{ key: "Monday", values: [ { key: "plain", values: [{day: 'Monday', donut: 34, variety: "plain"}]} ]}, { key:"Tuesday", values: [ { key: "glazed", values: [{day: 'Tuesday', donut: 41, variety: "glazed"}]} ] } ]
d3.time.format("%Y-%m-%d"); //2015-08-26 d3.format("+,%"); //+2,400%JSFiddle d3.time.format - creates a new local time formatter for a given specifier.
d3.time.scale - constructs a linear time scale.
var xScale = d3.time.scale() .domain([2009-07-13T00:02, 2009-07-13T23:48]) .rangeRound([0, width]); //rangeRound does the same thing as range but rounds the values to integers or begining of dates.JSFiddle
d3.time.intervals - a time interval in local time.
Slides : bit.ly/sfhtml5-d3