Lizzi Slivinski
April 12, 2017


eslivinski.github.io/d3.js
eslivinski.github.io/d3.js/examples.html

Today's Game Plan:

  1. d3 background
  2. svg basics
  3. editing svgs with d3
  4. mapping with d3

= Data Driven Documents

SVG


SVG Elements
SVG Attributes

Basic SVG Shapes


eslivinski.github.io/d3.js/examples.html

svg-basics / circles


  
  
    
    
    
  
          
codepen.io/eslivinski/pen/bqRZex

svg-basics / rectangles


  
    
    
    
  
          
codepen.io/eslivinski/pen/qrjGdw

svg-basics / text

Hello World Hooray SVGs

  
  
  
    
    Hello World
    Hooray SVGs
  
          
codepen.io/eslivinski/pen/JWJqGz

Modifying SVGs
with d3.js

modifying-svgs / general-ideas


  // SELECT ITEMS TO MANIPULATE
  var circles = d3.selectAll('circle');
          

  var circles = d3.selectAll('circle');

  // DEFINE PROPERTIES AND ATTRIBUTES TO MODIFY
  circles.attr('stroke', 'black');
  circles.attr('stroke-width', 1);
          

  var circles = d3.selectAll('circle');

  // CHAINING METHODS
  circles
    .attr('stroke', 'black')
    .attr('stroke-width', 1)
    .attr('radius', 100)
    .attr('cy', 100);
          

modifying-svgs / d3.select, d3.attr


  // STEP #1: Identify the element wish to modify
  var circle = d3.select('circle');

  // STEP #2: Use the attr method to change the element's fill
  // from blue to orange
  circle.attr('fill', 'orange');

          
codepen.io/eslivinski/pen/zZzQoy

modifying-svgs / d3.selectAll


  // Select will only grab the first element matches the selection
  d3.select('circle')
    .attr('fill', 'orange');

  // selectAll will grab all elements that match the description
  d3.selectAll('circle')
    .attr('fill', 'purple');

            
codepen.io/eslivinski/pen/GWEarq

Selectors

 element
-->
   
  #id
-->
  
  .class
-->
  
  [attribute = "value"]
-->
  
  parent > child
-->
    
  #ancestor  .descendent
-->
  
    ...
      
    ...
   

Selectors


Select Me Practice Selecting This

  
    Select Me
  
                

  // Pure Javscript
  document.querySelector('foo#bar');

  // Jquery
  $('foo#bar');

  // d3
  d3.select('foo#bar');
                

Animating SVGs


#transitions-d3-transition

animating-svgs / d3.transition


  // STEP #1: Make the circle bigger
  // Attr changes called after .transition will happen
  // gradually
  d3.select('circle')
    .transition()
    .attr('r', 100);

  // STEP #2: Make the circle purple as it moves to the right
  // If multiple attr changes occur after .transition they
  // happen at the same time
  d3.select('circle')
    .transition()
    .attr('cx', 500)
    .attr('fill', "purple");

  // STEP #3: Make the circle hot pink then make it smaller
  // Separating groups of attr changes with .transition
  // method calls will create stepped animations
  d3.select('circle')
    .transition()
    .attr('fill', '#E91E63')
    .transition()
    .attr('r', 10);

          
codepen.io/eslivinski/pen/jBwoaz

Data Binding

data-binding / d3.selection.data



  var my_data = [30];

  d3.select('circle')
    .data(my_data);

              
codepen.io/eslivinski/pen/zZzQRX

So what?

    Data-binding can help us define:

  1. How elements should look on the page
  2. Which elements should be on the page

data-binding / dynamic-attributes


  var my_data = [30]

  d3.select('circle')
    .data(my_data)
    .transition()
    .attr('r', function(d, i) {
      // whenever a function is passed into a method of
      // a d3 attribute function the first parameter (d)
      // always represents the data bound to that element
      // and the second (i) always represents the index
      // of that element in the selection
      return d;
    });
          
codepen.io/eslivinski/pen/NpgVYX

data-binding / complex-data-models


  var my_data = [
    { stat_1: 100, stat_2: 50 }
  ];

  d3.select('circle')
    .data(my_data)
    .transition()
    .attr('r', function(d, i) {
      return d.stat_1;
    })
    .attr('fill', function(d, i) {
      if (d.stat_2 >= 100) {
        return 'red';
      } else {
        return 'blue';
      }
    });
          
codepen.io/eslivinski/pen/NpgVMX

Creating
New
Elements

data-binding / d3.selection.data.enter


  // Create elements by adding an additional data object
  var my_data = [
    { stat_1: 40, stat_2: 10 },
    { stat_1: 30, stat_2: 250 },
    { stat_1: 130, stat_2: 40 }
  ];

  // Have to adjust to our selection to first select the
  // container then select the child elements we are
  // interested in and to use selectAll
  // even though there is currently only one circle
  var circles = d3.select('svg')
    .selectAll('circle')
    .data(my_data);

  // Only Happens to the new circles
  var newCircles = circles.enter()
    .append('circle');

  // Update the definition of circles var to include
  // the new circles
  circles = circles.merge(newCircles)
    .attr('cy', 100)
    .attr('cx', function(d, i) {
      return (i+1) * 200;
    })
    .transition()
    .attr('r', function(d, i) {
      return d.stat_1;
    })
    .attr('fill', function(d, i) {
      if (d.stat_2 >= 100) {
        return 'red';
      } else {
        return 'blue';
      }
    });
          
codepen.io/eslivinski/pen/bqRyzL

Removing Elements

data-binding / d3.selection.data.exit


  var my_data = [
    { stat_1: 150, stat_2: 300 },
    { stat_1: 30, stat_2: 115 }
  ];

  var circles = d3.select('svg')
    .selectAll('circle')
    .data(my_data);

  // Get rid of the extra circle
  circles.exit()
    .remove();

  // Won't be needed because the data is getting smaller
  // But we will include it for good measure
  var newCircles = circles.enter()
    .append('circle');

  circles.merge(newCircles)
    .attr('cy', 100)
    .attr('cx', function(d, i) {
      return (i+1) * 200;
    })
    .transition()
    .attr('r', function(d, i) {
      return d.stat_1;
    })
    .attr('fill', function(d, i) {
      if (d.stat_2 >= 100) {
        return 'red';
      } else {
        return 'blue';
      }
    });
          
codepen.io/eslivinski/pen/YZQbMp

data-binding / selecting-things-that-don't-exist


  var my_data = [
    { stat_1: 40, stat_2: 10 },
    { stat_1: 30, stat_2: 250 },
    { stat_1: 130, stat_2: 40 }
  ];

  // This looks like craziness, because there are no circles in the svg
  // but we are essentially creating a placeholder for circles
  // and handling the possibility that there might be circles
  var circles = d3.select('svg')
    .selectAll('circle')
    .data(my_data);

  var newCircles = circles.enter()
    .append('circle');

  circles.exit()
    .remove();

  circles = circles.merge(newCircles)
    .attr('cy', 100)
    .attr('cx', function(d, i) {
      return (i+1) * 200;
    })
    .transition()
    .attr('r', function(d, i) {
      return d.stat_1;
    })
    .attr('fill', function(d, i) {
      if (d.stat_2 >= 100) {
        return 'red';
      } else {
        return 'blue';
      }
    });
          
codepen.io/eslivinski/pen/MmWYXy

Mapping With d3.js

codepen.io/eslivinski/pen/WpOqeK

Drawing complex SVG shapes / SVG Paths

SVG Paths

Lizzi Slivinski

eslivinski@gmail.com
@eslivinski