{"id":22,"date":"2016-03-19T18:41:59","date_gmt":"2016-03-19T18:41:59","guid":{"rendered":"http:\/\/tawink.com\/?p=22"},"modified":"2016-03-19T18:41:59","modified_gmt":"2016-03-19T18:41:59","slug":"d3-js-simple-responsive-line-graph-with-animation","status":"publish","type":"post","link":"https:\/\/tawink.com\/?p=22","title":{"rendered":"d3.js Simple Responsive Line Graph with Animation"},"content":{"rendered":"<p>While introducing myself to the fantastic data visualization library d3.js I figured I might as well post some of the things I&#8217;ve learned.<\/p>\n<p>I needed a responsive line graph with just a little bit of animation to give my graph some life. I also wanted to be able to see details of the exact plot points on the graph. Lastly I wanted the graph to be able to animate if the data was ever swapped out for new data. After a little bit of work the result was the following graph below. The code is far from perfect but it&#8217;s definitely a starting point.<\/p>\n<p class=\"codepen\" data-width=\"100%\" data-theme-id=\"0\" data-slug-hash=\"grgNMY\" data-default-tab=\"result\" data-user=\"tawink\">See the Pen <a href=\"http:\/\/codepen.io\/tawink\/pen\/grgNMY\/\">d3.js Simple Responsive Line Graph with Animation<\/a> by Tawin Kiethanom (<a href=\"http:\/\/codepen.io\/tawink\">@tawink<\/a>) on <a href=\"http:\/\/codepen.io\">CodePen<\/a>.<\/p>\n<p><script src=\"\/\/assets.codepen.io\/assets\/embed\/ei.js\" async=\"\"><\/script><br \/>\n<!--more--><br \/>\n<strong>Breaking down the basic line graph code.<\/strong><\/p>\n<p>This graph is made up of a few parts:<\/p>\n<ul>\n<li>Data (JS array)<\/li>\n<li>Axes (X axis and Y axis)<\/li>\n<li>Line (svg path with stroke)<\/li>\n<li>Area (the filled path area under the line)<\/li>\n<li>Dots (with detail popup)<\/li>\n<\/ul>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nfunction init(){\n  chartContainer = d3.select('.chart-container');\n  svg = chartContainer.append('svg');\n  marginContainer = svg.append('g').attr('class', 'margin-container');\n}\n<\/pre>\n<p>These 3 lines initialize our chart by selecting the designated div,  appending the SVG element and adding a <code>marginContainer<\/code> in the SVG. The <code>marginContainer<\/code> will add padding to all our content, coming right up. <\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nsvg.attr(&quot;width&quot;, width + margin.left + margin.right)\n    .attr(&quot;height&quot;, height + margin.top + margin.bottom);\n\nmarginContainer\n  .attr(&quot;transform&quot;, &quot;translate(&quot; + margin.left + &quot;,&quot; + margin.top + &quot;)&quot;);\n\nx = d3.time.scale().range( &#x5B; 0, width ] );\ny = d3.scale.linear().range( &#x5B; height, 0 ] );\nx.domain(d3.extent(data, function(d) { return d.date; }));\ny.domain(&#x5B;0, d3.max(data, function(d) { return d.value; }) * 1.25]);\n<\/pre>\n<p>In our <code>render()<\/code> function we setup the dimensions of our SVG and <code>marginContainer<\/code>. See how the margins are used to get the total width of the SVG and shift the <code>marginContainer<\/code> over. This provides space for our Axes in the near future. <code>x<\/code> and <code>y<\/code> are defined as a scale of time and numbers respectively. Domain is basically the min and max numbers of our graph. For our <code>x axis<\/code> we want to show only the dates from the given dataset. For the <code>y axis<\/code> we want to start from 0 and go up to about 125% of the maximum number, this gives us some padding on the top of the chart. <\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\narea = d3.svg.area()\n  .x( function( d )  { return x( d.date ); } )\n  .y0( height )\n  .y1( function( d ) { return y( d.value ); } );\n\nline = d3.svg.area()\n  .x( function( d )  { return x( d.date ); } )\n  .y( function( d ) { return y( d.value ); } );\n\nstartData = data.map( function( datum ) {\n  return {\n    date  : datum.date,\n    value : 0\n  };\n});\n\nxAxis = d3.svg.axis()\n  .scale(x)\n  .orient('bottom');\n\nyAxis = d3.svg.axis()\n  .scale(y)\n  .orient('left');\n<\/pre>\n<p>Now we need functions to generate area and line. For area we need to generate a vertical sliver. So the first point <code>y0<\/code> is the height because everything is reversed when you&#8217;re working with SVG. The coordinates 0,0 start in the top left corner. So <code>y0<\/code> is essentially from the bottom. <code>y1<\/code> is the height of our data point. The line function is similar but we just need a point so its the <code>x<\/code> and <code>y<\/code> value. Then initialize the <code>startData<\/code> to zeroes and setup our <code>xAxis<\/code> and <code>yAxis<\/code>. <\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nmarginContainer.append('g')\n  .attr('class', 'x axis')\n  .attr('transform', 'translate(0,' + height + ')')\n  .call(xAxis);\n\nmarginContainer.append('g')\n  .attr('class', 'y axis')\n  .call(yAxis)\n  .append('text')\n  .attr('transform', 'rotate(-90)')\n  .attr('y', '1.5em')\n  .style('text-anchor', 'end')\n  .text('Price ($)');\n\nmarginContainer.append( 'path' )\n  .datum( startData )\n  .attr('class', 'line')\n  .attr( 'd', line )\n  .transition()\n  .duration( 500 )\n  .ease('quad')\n  .attrTween( 'd', function() {\n    var interpolator = d3.interpolateArray( startData, data );\n\n    return function( t ) {\n      return line( interpolator( t ) );\n    }\n  } )\n  .each('end', function() {\n    drawCircles( data, marginContainer );\n  });\n\nmarginContainer.append( 'path' )\n  .datum( startData )\n  .attr('class', 'area')\n  .attr( 'd', area )\n  .transition()\n  .duration( 500 )\n  .ease('quad')\n  .attrTween( 'd', function() {\n    var interpolator = d3.interpolateArray( startData, data );\n\n    return function( t ) {\n      return area( interpolator( t ) );\n    }\n  } );\n<\/pre>\n<p>Now we append all our parts, <code>x axis<\/code>, <code>y axis<\/code>, <code>line<\/code> and <code>area<\/code>. We append the <code>x axis<\/code> as a g element. We vertically translate it down to the bottom of the chart using the height (remember reversed coordinates). Then we call our previously create <code>xAxis<\/code> which creates the tick marks with values. The <code>y axis<\/code> doesn&#8217;t need to be translated, but we add a small text element. <\/p>\n<p>The <code>line<\/code> and <code>area<\/code> are going to be animated. To start we append a path using the initial <code>startData<\/code>. <code>transition()<\/code> tells the element theres going to be a transition. <code>attrTween()<\/code> is where the animation actually happens through interpolation. We want to go from zero to our data value so we pass in <code>startData<\/code> and <code>data<\/code>. You&#8217;ll notice after we append the line we also run a function called <code>drawCircles()<\/code>. <\/p>\n<p>We&#8217;ll get into appending circles and finishing off our chart in the next post. Cheers!<\/p>\n<p><a href=\"http:\/\/codepen.io\/tawink\/pen\/grgNMY\" target=\"_blank\" rel=\"noopener\">Codepen<\/a><\/p>\n<p><a href=\"https:\/\/github.com\/tkiethanom\/d3-samples\/tree\/master\/line-chart\" target=\"_blank\" rel=\"noopener\">Github<\/a><\/p>\n<p>Check out <a href=\"http:\/\/tawink.com\/d3-js-simple-responsive-line-graph-with-animation-part-2\/\">Part 2<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>While introducing myself to the fantastic data visualization library d3.js I figured I might as well post some of the things I&#8217;ve learned. I needed a responsive line graph with just a little bit of animation to give my graph some life. I also wanted to be able to see details of the exact plot [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[5,6,9],"class_list":["post-22","post","type-post","status-publish","format-standard","hentry","category-development","tag-code","tag-d3-js","tag-javascript"],"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/tawink.com\/index.php?rest_route=\/wp\/v2\/posts\/22","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tawink.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tawink.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tawink.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tawink.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=22"}],"version-history":[{"count":0,"href":"https:\/\/tawink.com\/index.php?rest_route=\/wp\/v2\/posts\/22\/revisions"}],"wp:attachment":[{"href":"https:\/\/tawink.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=22"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tawink.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=22"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tawink.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=22"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}