Skip to content

Instantly share code, notes, and snippets.

@ruedap
Last active August 29, 2015 13:59
Show Gist options
  • Save ruedap/10475534 to your computer and use it in GitHub Desktop.
Save ruedap/10475534 to your computer and use it in GitHub Desktop.
D3 example: basic bar chart with frame border
class D3Example
constructor: (@selector, width, height, @margin) ->
@el = @defineRootElement(@selector, +width, +height, @margin)
defineRootElement: (selector, width, height, margin) =>
@width = width - margin.left - margin.right
@height = height - margin.top - margin.bottom
d3.select(selector)
.append('svg')
.attr('width', width)
.attr('height', height)
.append('g')
.attr('transform', "translate(#{margin.left},#{margin.top})")
.attr('class', 'margin-convention-element')
render: (el = @el, w = @width, h = @height, margin = @margin) =>
data = [100, 50, 40, 20, 130]
xScale = d3.scale.ordinal().domain(d3.range(data.length))
xScale.rangeRoundBands([0, w], 0.1)
yScale = d3.scale.linear().domain([0, d3.max(data)])
yScale.range([0, h])
el.selectAll('g')
.data(data)
.enter()
.append('g')
.attr('class', 'data')
.append('rect')
.attr('x', (d, i) -> xScale(i))
.attr('width', xScale.rangeBand())
.attr('y', (d) -> h - yScale(d))
.attr('height', (d) -> yScale(d))
.attr('fill', '#cadce5')
el.selectAll('.data')
.append('path')
.attr('d', (d, i) -> rectBorderPath(d, i, h, xScale, yScale))
.attr('fill', 'none')
.attr('stroke', '#9eb1cc')
.attr('stroke-width', 3)
rectBorderPath = (d, i, h, xScale, yScale) ->
_x = xScale(i)
_y = h - yScale(d)
_w = xScale.rangeBand()
_h = yScale(d)
_data = [ [_x, h], [_x, _y], [_x + _w, _y], [_x + _w, h] ]
d3.svg.line().x((d) -> d[0]).y((d) -> d[1])(_data)
margin = top: 20, right: 10, bottom: 20, left: 10
new D3Example('body', '700', '394', margin).render()
@ruedap
Copy link
Author

ruedap commented Apr 11, 2014

D3.js(SVG)のrect要素でそのままstroke属性を適用すると4辺を囲ってしまうけど、下辺以外の3辺のみ描画したい場合の方法について、別途そのように線を引いたpath要素を上に重ねて実現するとして、上記のrectBorderPath()のようなSVGそのままっぽい書き方しか無いんだろうか?
d3.svg.line()でもっとお手軽に書けそうな予感がするけど果たして…

@ruedap
Copy link
Author

ruedap commented Apr 12, 2014

前述の書き方ではそもそも棒グラフとしての高さが変わってしまうからダメで、rect要素の内側に線を引くためにまずはpath要素で描画するようにしてみたのが以下。これをさらに改良してline要素で引こうとすると小数点の配置が必要になりそう。

  rectBorderPath = (d, i, h, xScale, yScale, sw) ->
    _x = xScale(i)
    _y = h - yScale(d)
    _w = xScale.rangeBand()
    _h = yScale(d)
    _data = [
      { x: _x, y: h },
      { x: _x, y: _y },
      { x: _x + _w, y: _y },
      { x: _x + _w, y: h },
      { x: _x + _w - sw, y: h },
      { x: _x + _w - sw, y: _y + sw },
      { x: _x + sw, y: _y + sw },
      { x: _x + sw, y: h },
    ]
    d3.svg.line().x((d) -> d.x).y((d) -> d.y)(_data) + 'z'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment