简体   繁体   中英

d3.js: Bar chart overflows labels

I've got a d3 bar chart with a height of 270 pixels. The chart renders fine, but it overruns the x-axis labels.

When I attempt to adjust either the y or height attributes of the bars, they overrun the bounds of the svg , either at the topmost (adjust height ), or bottom-most (adjust y ). How can I give the bars a bottom "margin," which will accommodate the labels?

Function which builds the chart:

  buildTimeline: ->
    width = @ui.chartWrap.width()
    height = @ui.chartWrap.height()
    console.log height
    dataLength = @data.values.length
    x = d3.time.scale()
      .domain(
        [
          new Date "#{@data.values[0].x}-1-1"
          new Date("#{@data.values[dataLength - 1].x}-1-1")
        ]
      ).rangeRound [0, width - (width/dataLength - 1)]

    y = d3.scale.linear()
      .domain(
        [
          0
          d3.max(@data.values, (d) ->
            d.y
          )
        ]
      ).rangeRound [height, 0]

    xAxis = d3.svg.axis()
      .scale(x)
      .orient('bottom')
      .ticks(d3.time.years, 1)
      .tickFormat(d3.time.format('%Y'))

    yAxis = d3.svg.axis()
      .scale(y)
      .orient('left')
      .tickPadding(8)

    svg = d3.select(@ui.chartWrap[0]).append('svg')
        .attr('width', width)
        .attr('height', height)

    # Bars
    svg.append('g')
      .attr('id', 'timeline-bars')
      .selectAll(".bar")
      .data(@data.values)
      .enter().append("rect")
      .attr("class", "bar")
      .attr("x", (d) ->
        x(new Date "#{d.x}-1-1")
      )
      .attr("width", width/dataLength - 1)
      .attr("y", (d) ->
        y(d.y)
      )
      .attr("height", (d) =>
        height - y(d.y)
      ).attr("data-date", (d) =>
        d.x
      ).attr("data-count", (d) =>
        d.y
      )

    svg.append("g")
      .attr('id', 'timeline-x-labels')
      .call(xAxis)

CSS(SASS) for the chart:

  #timeline
    height: 270px
    margin: 10px
    cursor: crosshair
    font-size: 1em

    .bar
      fill: blue
      shape-rendering: crispEdges
      &:hover
        cursor: pointer
        fill: darken(blue, 10)

    #timeline-x-labels
      +translate(0, 240px)
      path, line
        fill: none
        stroke: shade(gainsboro, 10%)
        shape-rendering: crispEdges

      .tick > text
        font-size: 0.7em
        +user-select(none)
        +transform(translate(0px, 10px) rotate(-60deg))

Instead of adjusting the 'x' and 'y' attributes of your graph, try changing the ranges on your scales. If you draw a scale with .range(50, width) (or .rangeround(50, width) ) instead of (0,width) , you will effectively give yourself a 50px margin.

I ended up rewriting the whole code based on the tutorial here: http://bost.ocks.org/mike/bar/3/

Final product looks like this:

  buildTimeline: ->
    margin = 
      top: 10
      bottom: 30
      left: 40
      right: 10
    width = @ui.chartWrap.width() - margin.left - margin.right
    height = @ui.chartWrap.height() - margin.top - margin.bottom
    dataLength = @data.values.length

    y = d3.scale.linear()
      .range([height, 0])

    x = d3.scale.ordinal()
      .rangeRoundBands([0, width], 0.1)

    xAxis = d3.svg.axis()
      .scale(x)
      .orient 'bottom'

    yAxis = d3.svg.axis()
      .scale(y)
      .orient('left')
      .tickFormat (d) ->
        prefix = d3.formatPrefix d
        prefix.scale(d) + prefix.symbol

    @chart = d3.select(@ui.chartWrap[0]).append('svg')
      .attr('width', width + margin.left + margin.right)
      .attr('height', height + margin.top + margin.bottom)
      .append('g').attr('transform', "translate(#{margin.left}, #{margin.top})")

    x.domain _.pluck(@data.values, 'x')
    y.domain [0, d3.max(_.pluck(@data.values, 'y'))]

    @chart.append('g')
      .attr('id', 'timeline-x-axis')
      .attr('class', 'axis')
      .attr('transform', "translate(0, #{height})")
      .call xAxis

    @chart.append('g')
      .attr('class', 'axis')
      .call yAxis

    @chart.selectAll('.bar')
      .data(@data.values)
      .enter().append('rect')
      .attr('class', 'bar')
      .attr('x', (d) ->
        x(d.x)
      )
      .attr('y', (d) ->
        y(d.y)
      )
      .attr('height', (d) ->
        height - y(d.y)
      )
      .attr('width', x.rangeBand())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM