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.