{"id":64,"date":"2016-03-23T21:00:32","date_gmt":"2016-03-23T21:00:32","guid":{"rendered":"http:\/\/tawink.com\/?p=64"},"modified":"2016-03-23T21:00:32","modified_gmt":"2016-03-23T21:00:32","slug":"d3-js-simple-responsive-line-graph-with-animation-part-2","status":"publish","type":"post","link":"https:\/\/tawink.com\/?p=64","title":{"rendered":"d3.js Simple Responsive Line Graph with Animation: Part 2"},"content":{"rendered":"<p>I&#8217;m back for another addition of d3.js for noobs. I&#8217;m a professional developer but I&#8217;m no d3.js pro, but I believe I&#8217;ve learned a few things that could be useful.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\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<\/pre>\n<p>So last time we got to the point where we&#8217;re calling the <code>drawCircles()<\/code> function. When you use <code>.each('end', function(){<\/code> the function gets called at the end of each selection. In this case we only have one selection.<\/p>\n<p><!--more--><\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nfunction drawCircles( data, container ) {\n  circleContainer = container.append( 'g').attr('class', 'circles');\n  data.forEach( function( datum, index ) {\n    drawCircle( datum, index );\n  } );\n}\n<\/pre>\n<p>The <code>drawCircles()<\/code> function is simple. It appends the <code>circleContainer<\/code> and it takes the passed in data and calls the <code>drawCircle()<\/code> function for each data point.  <\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nfunction drawCircle( datum, index ) {\n  circleContainer.datum( datum )\n    .append( 'circle' )\n    .attr( 'class', 'circle' )\n    .attr( 'r', 0 )\n    .attr(\n      'cx',\n      function( d ) {\n        return x( d.date );\n      }\n    )\n    .attr(\n      'cy',\n      function( d ) {\n        return y( d.value );\n      }\n    )\n    .on( 'mouseenter', function( d ) {\n      d3.select( this )\n        .attr(\n          'class',\n          'circle active'\n        )\n        .attr( 'r', 7 );\n\n      d.active = true;\n\n      showCircleDetail( d );\n    } )\n    .on( 'mouseout', function( d ) {\n      d3.select( this )\n        .attr(\n          'class',\n          'circle'\n        )\n        .attr( 'r', 6 );\n\n      if ( d.active ) {\n        hideCircleDetails();\n\n        d.active = false;\n      }\n    } )\n    .on( 'click touch', function( d ) {\n      if ( d.active ) {\n        showCircleDetail( d )\n      } else {\n        hideCircleDetails();\n      }\n    } )\n    .transition()\n    .delay( 100 * index )\n    .duration(750)\n    .ease('elastic', 1.5, .75)\n    .attr( 'r', 6 )\n  ;\n}\n<\/pre>\n<p>The <code>drawCircle()<\/code> function its pretty long but most of it is just handling the mouse events. First create a circle using the <code>datum<\/code>. The <code>r<\/code> attribute is the circle radius. The <code>cx<\/code> and <code>cy<\/code> attributes handle the x and y coordinates of the circle, <code>date<\/code> and <code>value<\/code> respectively. <\/p>\n<p>Now to handle our mouse events. On <code>mouseenter<\/code> we chance the class of the circe to include <code>active<\/code>. We increase the radius a smidge. Finally we call the function to show the detail popover. On <code>mouseout<\/code> we reverse the <code>mouseenter<\/code> changes. We also handle touch events with <code>click touch<\/code>.<\/p>\n<p>At the end we handle the initial animation for the circles. The <code>.delay()<\/code> function creates the delay between each created point. We add some fun easing to make the circles look like they are plopping in. The <code>1.5<\/code> sets the percentage of the the max size of the elastic animation and the <code>.75<\/code> is the min percentage. <\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nfunction showCircleDetail( data ) {\n  var details = circleContainer.append( 'g' )\n    .attr( 'class', 'bubble' )\n    .attr(\n      'transform',\n      function() {\n        var result = 'translate(';\n\n        var xVal = x( data.date ) - detailWidth\/2;\n        if(xVal + detailWidth &gt; width ){\n          xVal = width - detailWidth;\n        }\n        else if(xVal &lt; 0){\n          xVal = 0;\n        }\n\n        result += xVal;\n        result += ', ';\n        result += y( data.value ) - detailHeight - detailMargin;\n        result += ')';\n\n        return result;\n      }\n    );\n\n  details.append( 'rect' )\n    .attr( 'width', detailWidth )\n    .attr( 'height', detailHeight )\n    .attr('rx', 5)\n    .attr('ry', 5);\n\n  var text = details.append( 'text' )\n    .attr( 'class', 'text' );\n\n  var dateFormat = d3.time.format(&quot;%m\/%d\/%Y&quot;);\n\n  text.append( 'tspan' )\n    .attr( 'class', 'price' )\n    .attr( 'x', detailWidth \/ 2 )\n    .attr( 'y', detailHeight \/ 3 )\n    .attr( 'text-anchor', 'middle' )\n    .text( 'Price: ' + data.value );\n\n  text.append( 'tspan' )\n    .attr( 'class', 'date' )\n    .attr( 'x', detailWidth \/ 2 )\n    .attr( 'y', detailHeight \/ 4 * 3 )\n    .attr( 'text-anchor', 'middle' )\n    .text( 'Date: ' + dateFormat(data.date) );\n}\n<\/pre>\n<p>Finally the <code>showCircleDetail()<\/code> function is what creates the popover when you hover over a data point on the line. First we append a new element called details. We&#8217;re going to use css <code>transform<\/code> <code>translate()<\/code> to position the popover in  the right place.<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\nif(xVal + detailWidth &gt; width ){\n  xVal = width - detailWidth;\n}\nelse if(xVal &lt; 0){\n  xVal = 0;\n}\n<\/pre>\n<p>This piece of code may seem unclear but the reason we&#8217;re doing this is to ensure our popover is not being placed outside of the width of graph. So at most it&#8217;ll be touching the left or right edge of our graph.<\/p>\n<p>Then we create our actual SVG parts, the rectangle as the background\/container and the text. The text is broken up using <code>tspan<\/code> to position them. <\/p>\n<p>That&#8217;s all for now. Don&#8217;t forget to check out <a href=\"http:\/\/tawink.com\/d3-js-simple-responsive-line-graph-with-animation\/\">Part 1<\/a> if you missed it. 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","protected":false},"excerpt":{"rendered":"<p>I&#8217;m back for another addition of d3.js for noobs. I&#8217;m a professional developer but I&#8217;m no d3.js pro, but I believe I&#8217;ve learned a few things that could be useful. marginContainer.append( &#8216;path&#8217; ) .datum( startData ) .attr(&#8216;class&#8217;, &#8216;line&#8217;) .attr( &#8216;d&#8217;, line ) .transition() .duration( 500 ) .ease(&#8216;quad&#8217;) .attrTween( &#8216;d&#8217;, function() { var interpolator = d3.interpolateArray( [&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-64","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\/64","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=64"}],"version-history":[{"count":0,"href":"https:\/\/tawink.com\/index.php?rest_route=\/wp\/v2\/posts\/64\/revisions"}],"wp:attachment":[{"href":"https:\/\/tawink.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=64"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tawink.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=64"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tawink.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=64"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}