block by renecnielsen c0869bddcd899bd9a148

Responsive Line Chart Example

Full Screen

forked from chrtze‘s block: Responsive Line Chart Example

index.html

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width" />
  <title>D3 Line Chart</title>
  <link rel="stylesheet" href="style.css">
  <script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
  <div id="chart"></div>
  <script src="chart.js"></script>
</body>
</html>

chart.js

var Chart = (function(window,d3) {

  var svg, data, x, y, xAxis, yAxis, dim, chartWrapper, line, path, margin = {}, width, height, locator;

  var breakPoint = 768;

  d3.csv('data.csv', init); //load data, then initialize chart
  
  //called once the data is loaded
  function init(csv) {
    data = csv;

    //initialize scales
    xExtent = d3.extent(data, function(d,i) { return new Date(d.date) });
    yExtent = d3.extent(data, function(d,i) { return d.value });
    x = d3.time.scale().domain(xExtent);
    y = d3.scale.linear().domain(yExtent);

    //initialize axis
    xAxis = d3.svg.axis().orient('bottom');
    yAxis = d3.svg.axis();

    //the path generator for the line chart
    line = d3.svg.line()
      .x(function(d) { return x(new Date(d.date)) })
      .y(function(d) { return y(d.value) });

    //initialize svg
    svg = d3.select('#chart')
      .append('svg')
      .style('pointer-events', 'none');


    chartWrapper = svg
      .append('g')
      .style('pointer-events', 'all');

    path = chartWrapper.append('path').datum(data).classed('line', true);

    chartWrapper.append('g').classed('x axis', true);
    chartWrapper.append('g').classed('y axis', true);

    chartWrapper.on('touchmove', onTouchMove);

    //add locator
    locator = chartWrapper.append('circle')
      .style('display', 'none')
      .attr('r', 10)
      .attr('fill', '#f00');

    touchScale = d3.scale.linear();

    //render the chart
    render();
  }

  function render() {

    //get dimensions based on window size
    updateDimensions(window.innerWidth);
    
    //update x and y scales to new dimensions
    x.range([0, width]);
    y.range([height, 0]);

    touchScale.domain([0,width]).range([0,data.length-1]).clamp(true);

    //update svg elements to new dimensions
    svg
      .attr('width', width + margin.right + margin.left)
      .attr('height', height + margin.top + margin.bottom);

    chartWrapper
      .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

    //update the axis and line
    xAxis.scale(x);
    yAxis.scale(y).orient(window.innerWidth < breakPoint ? 'right' : 'left');

    if(window.innerWidth < breakPoint) {
      xAxis.ticks(d3.time.month, 2)
    }
    else {
      xAxis.ticks(d3.time.month, 1)
    }
    
    svg.select('.x.axis')
      .attr('transform', 'translate(0,' + height + ')')
      .call(xAxis);

    svg.select('.y.axis')
      .call(yAxis);

    path.attr('d', line);
    renderLabels();
  }

  var labels = [
    {
      x: new Date('03-15-2014'),
      y: .17,
      text: 'Test Label 1',
      orient: 'right'
    },
    {
      x: new Date('11-20-2014'),
      y: .24,
      text: 'Test Label 2',
      orient: 'left'
    }
  ]

  function renderLabels() {

    var _labels = chartWrapper.selectAll('text.label');

    if(_labels[0].length > 0) {
      //labels already exist
      _labels
        .attr('x', function(d) { return x(d.x) })
        .attr('y', function(d) { return y(d.y) })
    }
    else {
      //append labels if function is called for the first time
      _labels
        .data(labels)
        .enter()
        .append('text')
        .classed('label', true)
        .attr('x', function(d) { return x(d.x) })
        .attr('y', function(d) { return y(d.y) })
        .style('text-anchor', function(d) { return d.orient == 'right' ? 'start' : 'end' })
        .text(function(d) { return d.text });
    }
  }

  function updateDimensions(winWidth) {
    margin.top = 20;
    margin.right = winWidth < breakPoint ? 0 : 50;
    margin.left = winWidth < breakPoint ? 0 : 50;
    margin.bottom = 50;

    width = winWidth - margin.left - margin.right;
    height = .7 * width;
  }

  function onTouchMove() {
    var xPos = d3.touches(this)[0][0];
    var d = data[~~touchScale(xPos)];

    locator.attr({
      cx : x(new Date(d.date)),
      cy : y(d.value)
    })
    .style('display', 'block');
  }

  return {
    render : render
  }

})(window,d3);

window.addEventListener('resize', Chart.render);

create-data.js

var fs = require('fs');
var startDate = new Date('2014-1-1');
var endDate = new Date('2015-1-1');
var csv = 'date,value\n';

for(var i = startDate; i < endDate; startDate.setDate(startDate.getDate() + 10)) {
  csv += '' + startDate.toString() + ',' + Math.random() + '\n';
}

fs.writeFileSync('data.csv', csv);

data.csv

date,value
Wed Jan 01 2014 00:00:00 GMT+0100 (CET),0.505003142170608
Sat Jan 11 2014 00:00:00 GMT+0100 (CET),0.5459181617479771
Tue Jan 21 2014 00:00:00 GMT+0100 (CET),0.14592946274206042
Fri Jan 31 2014 00:00:00 GMT+0100 (CET),0.7082753519061953
Mon Feb 10 2014 00:00:00 GMT+0100 (CET),0.010393543168902397
Thu Feb 20 2014 00:00:00 GMT+0100 (CET),0.6583312929142267
Sun Mar 02 2014 00:00:00 GMT+0100 (CET),0.24273463618010283
Wed Mar 12 2014 00:00:00 GMT+0100 (CET),0.15577656053937972
Sat Mar 22 2014 00:00:00 GMT+0100 (CET),0.8833602059166878
Tue Apr 01 2014 00:00:00 GMT+0200 (CEST),0.26687808078713715
Fri Apr 11 2014 00:00:00 GMT+0200 (CEST),0.649613720132038
Mon Apr 21 2014 00:00:00 GMT+0200 (CEST),0.4543698029592633
Thu May 01 2014 00:00:00 GMT+0200 (CEST),0.7719218074344099
Sun May 11 2014 00:00:00 GMT+0200 (CEST),0.5177543167956173
Wed May 21 2014 00:00:00 GMT+0200 (CEST),0.9994703007396311
Sat May 31 2014 00:00:00 GMT+0200 (CEST),0.7990491802338511
Tue Jun 10 2014 00:00:00 GMT+0200 (CEST),0.2791274816263467
Fri Jun 20 2014 00:00:00 GMT+0200 (CEST),0.00961384573020041
Mon Jun 30 2014 00:00:00 GMT+0200 (CEST),0.17923940671607852
Thu Jul 10 2014 00:00:00 GMT+0200 (CEST),0.5797557467594743
Sun Jul 20 2014 00:00:00 GMT+0200 (CEST),0.41668077907525003
Wed Jul 30 2014 00:00:00 GMT+0200 (CEST),0.26668014749884605
Sat Aug 09 2014 00:00:00 GMT+0200 (CEST),0.9645155349280685
Tue Aug 19 2014 00:00:00 GMT+0200 (CEST),0.9435918028466403
Fri Aug 29 2014 00:00:00 GMT+0200 (CEST),0.4980645985342562
Mon Sep 08 2014 00:00:00 GMT+0200 (CEST),0.9871973677072674
Thu Sep 18 2014 00:00:00 GMT+0200 (CEST),0.8283164533786476
Sun Sep 28 2014 00:00:00 GMT+0200 (CEST),0.6416109406854957
Wed Oct 08 2014 00:00:00 GMT+0200 (CEST),0.6466128702741116
Sat Oct 18 2014 00:00:00 GMT+0200 (CEST),0.7002710588276386
Tue Oct 28 2014 00:00:00 GMT+0100 (CET),0.9069056580774486
Fri Nov 07 2014 00:00:00 GMT+0100 (CET),0.6439003541599959
Mon Nov 17 2014 00:00:00 GMT+0100 (CET),0.7253473403397948
Thu Nov 27 2014 00:00:00 GMT+0100 (CET),0.22283505811356008
Sun Dec 07 2014 00:00:00 GMT+0100 (CET),0.6578892532270402
Wed Dec 17 2014 00:00:00 GMT+0100 (CET),0.14639883721247315
Sat Dec 27 2014 00:00:00 GMT+0100 (CET),0.31333006569184363

style.css

body {
  font: 12px sans-serif;
  margin: 0;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.x.axis path {
  display: none;
}

.line {
  fill: none;
  stroke: steelblue;
  stroke-width: 1.5px;
}