Area Chart main source: ··· Home

Notes:

  • Changed style
  • Added point and voronoi functionality

Data: data.csv

Code:

  • area.ejs

    <%  demo = {category: "d3js", key: "area", files: files}; %>
    
    <%- partial("../mixins/_demo_title", demo) %>
    
    <div id="chart" class="<%= demo.key %>-chart"></div>
    
    <script type="text/javascript" src="/vendors/bower/d3/d3.min.js"></script>
    <script type="text/javascript" src="/js/d3js-utils.js"></script>
    <script type="text/javascript" src="/js/d3js/<%= demo.key %>.js"></script>
    
    <%- partial("../mixins/_notes", demo) %>
    <%- partial("../mixins/_sources_data", demo) %>
    <%- partial("../mixins/_code", demo) %>
  • area.coffee

    ready = ( ->
      d3.csv('/data/d3js/area/data.csv', (error, data)->
        margin = {top: 50, right: 50, bottom: 50, left: 70}
        width = $('#chart').innerWidth() - margin.left - margin.right
        height = 400 - margin.top - margin.bottom
    
        svg = d3utils.svg('#chart', width, height, margin)
        d3utils.middleTitle(svg, width, 'Share of top decile [aka top 10%] in national income')
        d3utils.filterBlackOpacity('points', svg, 2, .5)
    
        xMax = d3.max(data, (d)-> d.year )
        xMin = d3.min(data, (d)-> d.year )
        yMax = d3.max(data, (d)-> d.percent / 100 )
        yMin = d3.min(data, (d)-> d.percent / 100 )
        yExtent = d3.extent(data, (d)-> d.percent)
    
        xScale = d3.scale.linear().range([0, width ]).domain([xMin, xMax])
        yScale = d3.scale.linear().range([0, height]).domain([yMax + .05, yMin - 0.05])
        
        xAxis = d3.svg.axis().scale(xScale).tickFormat(d3.format('.')).innerTickSize(-height)
        
        yAxis = d3.svg.axis().scale(yScale).tickFormat(d3.format('%'))
          .innerTickSize(-width).orient('left')
        
        svg.append('g').attr('class', 'x axis')
          .attr('transform', 'translate(' + 0 + ',' + \
            String(height) + ')')
          .call(xAxis).selectAll('text').attr({dy: '1.25em'})
        
        svg.append('g').attr('class', 'y axis').call(yAxis)
          .selectAll('text').attr({dx: '-.25em'})
         
        area = d3.svg.area().interpolate('monotone').x((d)-> xScale(d.year))
          .y0(height).y1((d)-> yScale(d.percent / 100))
    
        line = d3.svg.line()
          .x((d)-> xScale(d.year))
          .y((d)-> yScale(d.percent / 100))
    
        svg.append('path').datum(data).attr({class: 'line', 'clip-path': 'url(#clip)', \
          d: line })
    
        svg.append('clipPath').attr({id: 'clip'}).append('rect')
          .attr({width: width, height: height})
    
        svg.append('path').datum(data).attr({class: 'area', 'clip-path': 'url(#clip)', d: area})
    
        voronoiData = _.map(data, (point)->
          [point.year, ]
        )
    
        voronoi = d3.geom.voronoi().x((point)-> xScale(point.year))
          .y((point)-> yScale(point.percent / 100) )
          .clipExtent([[-margin.left, -margin.top], [width + margin.right, height + margin.bottom]])
    
        mouseover = ((d)->
          d3.select('.point-' + d.index).style('display', 'block')
        )
    
        mouseleave = ((d)->
          d3.select('.point-' + d.index).style('display', 'none')
        )
    
        svg.selectAll('circle').data(data).enter().append('circle')
          .attr('transform', (d)-> 'translate(' + String(xScale(d.year)) + \
            ',' + yScale(d.percent / 100) + ')')
          .attr('r', '5px').attr('class', (d,i)->'point point-' + i)
          .style({filter: 'url(#drop-shadow-points)'})
    
    
        voronoiGroup = svg.append('g').attr('class', 'voronoi')
        voronoiGroup.selectAll('path').data(voronoi(data))
          .enter().append('path').attr('d', (d,i)-> d.index = i; 'M' + d.join('L') + 'Z')
          .on('mouseover', mouseover).on('mouseleave', mouseleave)
          .attr('class', 'voronoi-area')
          .append('title').text((d)-> 'Year: ' + d.point.year + ': ' + \
            'Percent: ' + d.point.percent + '%')
      )
    )
    
    
    $(document).ready(ready)
  • _area.styl

    .area-chart
      path.line 
        fill none
        stroke #25A6FC
        stroke-width 3.5px
      
      path.area 
        fill #6E6EA7
        opacity .5
    
      text
        text-shadow 1px 1px 1px #ddd
      
      .axis 
        shape-rendering crispEdges
        fill #333
    
      .chart-title
        font-weight bold
      
      .x.axis .minor 
        fill #000
      
      .axis path,
      .axis line 
        fill none
        stroke #ccc
        shape-rendering crispEdges
    
      .y.axis line, .y.axis path, 
      .x.axis line, .x.axis path
        fill none
        stroke #ccc
    
      .voronoi
        stroke-width .1
        opacity 0
    
      .point
        fill #69D895
        display none
  • d3js-utils.coffee

    d3utils = {
      middleTitle: ((svg, width, text, top = -15)->
        element = svg.append('text').attr({class: 'chart-title', 'text-anchor': 'middle', \
          transform: 'translate(' + String(width / 2) + ',' + top + ')'})
          .text(text).style({'font-weight': 'bold'})
      )
    
      svg: ((selector, width, height, margin)->
        d3.select(selector).text('').append('svg')
          .attr({width: width + margin.left + margin.right, \
            height: height + margin.top + margin.bottom})
          .append('g').attr({transform: 'translate(' + margin.left + ',' + margin.top + ')'})
      )
    
      filterBlackOpacity: ((id, svg, deviation, slope)->
        defs = svg.append('defs')
        filter = defs.append('filter').attr({id: 'drop-shadow-' + id, width: '500%', height: '500%', \
          x: '-200%', y: '-200%'})
        filter.append('feGaussianBlur').attr({in: 'SourceAlpha', stdDeviation: deviation})
        filter.append('feOffset').attr({dx: 1, dy: 1})
        filter.append('feComponentTransfer').append('feFuncA').attr({type: 'linear', slope: slope})
        feMerge = filter.append('feMerge')
        feMerge.append('feMergeNode')
        feMerge.append('feMergeNode').attr('in', 'SourceGraphic')
      )
    
      filterColor: ((id, svg, deviation, slope, extra = false)->
        defs = svg.append('defs')
        filter = defs.append('filter').attr({id:'drop-shadow-' + id})
        filter.attr({width: '500%', height: '500%', x: '-200%', y: '-200%'}) if extra
        filter.append('feOffset').attr({result: 'offOut', in: 'SourceGraphic', dx: .5, dy: .5})
        filter.append('feGaussianBlur')
          .attr({result: 'blurOut', in: 'offOut', stdDeviation: deviation})
        filter.append('feBlend').attr({in: 'SourceGraphic', in2: 'blurOut', mode: 'normal'})
        filter.append('feComponentTransfer').append('feFuncA').attr({type: 'linear', slope: slope})
      )
    
    
    
      tooltip: ((selector, customOpts = {})->
        defaultOpts = {
          followMouse: false, followElement: false, elementSelector: ''
          leftOffst: 60, topOffst: 40
          tOpts: {container: 'body', viewport: {selector: '#chart svg'}}
        }
        opts = _.merge(defaultOpts, customOpts)
    
        # Bootstrap tooltip.
        $(selector).tooltip(opts.tOpts)
    
        # For SVG forms, it is better to position the tooltip manually.
        if opts.followMouse
          $(selector).hover((e)->
            $('.tooltip')
              .css({top: String(e.pageY - opts.topOffst) + 'px', \
                left: String(e.pageX - opts.leftOffst) + 'px'})
          )
        else if opts.followElement
          $(selector).hover((e)->
            $('.tooltip')
              .css({top: String($(opts.elementSelector).position().top - opts.topOffst) + 'px', \
                left: String($(opts.elementSelector).position().left - opts.leftOffst) + 'px'})
          )
      )
    
    
      colorsScale: ((colors, extent)->
        c = d3.scale.linear().domain(extent).range([0,1])
        colorScale = d3.scale.linear()
          .domain(d3.range(0, 1, 1.0 / (colors.length))).range(colors)
        return ((p)-> colorScale(c(p)))
      )
    
    }
    
    window.d3utils = d3utils

Home