draw functionality broken out into methods on each class

This commit is contained in:
Christopher Erin 2012-12-31 12:09:38 -06:00
parent 14c56165c6
commit 32b06226c2
7 changed files with 102 additions and 109 deletions

View file

@ -26,7 +26,7 @@ class Morris.Area extends Morris.Line
path = @paths[i] path = @paths[i]
if path isnt null if path isnt null
path = path + "L#{@transX(@xmax)},#{@bottom}L#{@transX(@xmin)},#{@bottom}Z" path = path + "L#{@transX(@xmax)},#{@bottom}L#{@transX(@xmin)},#{@bottom}Z"
@morrisSVG.drawFilledPath(path, @fillForSeries(i)) @drawFilledPath(path, @fillForSeries(i))
super() super()
fillForSeries: (i) -> fillForSeries: (i) ->
@ -35,3 +35,8 @@ class Morris.Area extends Morris.Line
color.h, color.h,
Math.min(255, color.s * 0.75), Math.min(255, color.s * 0.75),
Math.min(255, color.l * 1.25)) Math.min(255, color.l * 1.25))
drawFilledPath: (path, fill) ->
@raphael.path(path)
.attr('fill', fill)
.attr('stroke-width', 0)

View file

@ -59,7 +59,7 @@ class Morris.Bar extends Morris.Grid
prevLabelMargin = null prevLabelMargin = null
for i in [0...@data.length] for i in [0...@data.length]
row = @data[@data.length - 1 - i] row = @data[@data.length - 1 - i]
label = @morrisSVG.drawXAxisLabel(row._x, ypos, row.label) label = @drawXAxisLabel(row._x, ypos, row.label)
labelBox = label.getBBox() labelBox = label.getBBox()
# ensure a minimum of `xLabelMargin` pixels between labels, and ensure # ensure a minimum of `xLabelMargin` pixels between labels, and ensure
# labels don't overflow the container # labels don't overflow the container
@ -94,7 +94,7 @@ class Morris.Bar extends Morris.Grid
size = bottom - top size = bottom - top
top -= lastTop if @options.stacked top -= lastTop if @options.stacked
@morrisSVG.drawBar(left, top, barWidth, size, @colorFor(row, sidx, 'bar')) @drawBar(left, top, barWidth, size, @colorFor(row, sidx, 'bar'))
lastTop += size lastTop += size
else else
@ -152,3 +152,13 @@ class Morris.Bar extends Morris.Grid
""" """
x = @left + (index + 0.5) * @width / @data.length x = @left + (index + 0.5) * @width / @data.length
[content, x] [content, x]
drawXAxisLabel: (xPos, yPos, text) ->
label = @raphael.text(xPos, yPos, text)
.attr('font-size', @options.gridTextSize)
.attr('fill', @options.gridTextColor)
drawBar: (xPos, yPos, width, height, barColor) ->
@raphael.rect(xPos, yPos, width, height)
.attr('fill', barColor)
.attr('stroke-width', 0)

View file

@ -54,7 +54,7 @@ class Morris.Donut
redraw: -> redraw: ->
@el.empty() @el.empty()
@morrisSVG = new Morris.SVG(@el[0], @options) @raphael = new Raphael(@el[0])
cx = @el.width() / 2 cx = @el.width() / 2
cy = @el.height() / 2 cy = @el.height() / 2
@ -71,14 +71,14 @@ class Morris.Donut
@segments = [] @segments = []
for d in @data for d in @data
next = last + min + C * (d.value / total) next = last + min + C * (d.value / total)
seg = new Morris.DonutSegment(cx, cy, w*2, w, last, next, @options.colors[idx % @options.colors.length], d) seg = new Morris.DonutSegment(cx, cy, w*2, w, last, next, @options.colors[idx % @options.colors.length], d, @raphael)
seg.render @morrisSVG seg.render()
@segments.push seg @segments.push seg
seg.on 'hover', @select seg.on 'hover', @select
last = next last = next
idx += 1 idx += 1
@text1 = @morrisSVG.drawEmptyDonutLabel(cx, cy - 10, 15, 800) @text1 = @drawEmptyDonutLabel(cx, cy - 10, 15, 800)
@text2 = @morrisSVG.drawEmptyDonutLabel(cx, cy + 10, 14) @text2 = @drawEmptyDonutLabel(cx, cy + 10, 14)
max_value = Math.max.apply(null, d.value for d in @data) max_value = Math.max.apply(null, d.value for d in @data)
idx = 0 idx = 0
for d in @data for d in @data
@ -109,12 +109,17 @@ class Morris.Donut
text2scale = Math.min(maxWidth / text2bbox.width, maxHeightBottom / text2bbox.height) text2scale = Math.min(maxWidth / text2bbox.width, maxHeightBottom / text2bbox.height)
@text2.attr(transform: "S#{text2scale},#{text2scale},#{text2bbox.x + text2bbox.width / 2},#{text2bbox.y}") @text2.attr(transform: "S#{text2scale},#{text2scale},#{text2bbox.x + text2bbox.width / 2},#{text2bbox.y}")
drawEmptyDonutLabel: (xPos, yPos, fontSize, fontWeight) ->
text = @raphael.text(xPos, yPos, '').attr('font-size', fontSize)
text.attr('font-weight', fontWeight) if fontWeight?
return text
# A segment within a donut chart. # A segment within a donut chart.
# #
# @private # @private
class Morris.DonutSegment extends Morris.EventEmitter class Morris.DonutSegment extends Morris.EventEmitter
constructor: (@cx, @cy, @inner, @outer, p0, p1, @color, @data) -> constructor: (@cx, @cy, @inner, @outer, p0, p1, @color, @data, @raphael) ->
@sin_p0 = Math.sin(p0) @sin_p0 = Math.sin(p0)
@cos_p0 = Math.cos(p0) @cos_p0 = Math.cos(p0)
@sin_p1 = Math.sin(p1) @sin_p1 = Math.sin(p1)
@ -147,9 +152,18 @@ class Morris.DonutSegment extends Morris.EventEmitter
"M#{ix0},#{iy0}" + "M#{ix0},#{iy0}" +
"A#{r},#{r},0,#{@long},0,#{ix1},#{iy1}") "A#{r},#{r},0,#{@long},0,#{ix1},#{iy1}")
render: (morrisSVG) -> render: ->
@arc = morrisSVG.drawDonutArc(@hilight, @color) @arc = @drawDonutArc(@hilight, @color)
@seg = morrisSVG.drawDonutSegment(@path, @color, => @fire('hover', @)) @seg = @drawDonutSegment(@path, @color, => @fire('hover', @))
drawDonutArc: (path, color) ->
@raphael.path(path)
.attr(stroke: color, 'stroke-width': 2, opacity: 0)
drawDonutSegment: (path, color, hoverFunction) ->
@raphael.path(path)
.attr(fill: color, stroke: 'white', 'stroke-width': 3)
.hover(hoverFunction)
select: => select: =>
unless @selected unless @selected

View file

@ -26,8 +26,7 @@ class Morris.Grid extends Morris.EventEmitter
@options.postUnits = options.units @options.postUnits = options.units
# the raphael drawing instance # the raphael drawing instance
@r = new Raphael(@el[0]) @raphael = new Raphael(@el[0])
@morrisSVG = new Morris.SVG(@el[0], @options)
# some redraw stuff # some redraw stuff
@elementWidth = null @elementWidth = null
@ -228,7 +227,7 @@ class Morris.Grid extends Morris.EventEmitter
# If you need to re-size your charts, call this method after changing the # If you need to re-size your charts, call this method after changing the
# size of the container element. # size of the container element.
redraw: -> redraw: ->
@morrisSVG.clear() @raphael.clear()
@_calc() @_calc()
@drawGrid() @drawGrid()
@drawGoals() @drawGoals()
@ -239,12 +238,12 @@ class Morris.Grid extends Morris.EventEmitter
# #
drawGoals: -> drawGoals: ->
for goal, i in @options.goals for goal, i in @options.goals
@morrisSVG.drawGoal("M#{@left},#{@transY(goal)}H#{@left + @width}") @drawGoal("M#{@left},#{@transY(goal)}H#{@left + @width}")
# draw events vertical lines # draw events vertical lines
drawEvents: -> drawEvents: ->
for event, i in @events for event, i in @events
@morrisSVG.drawEvent("M#{@transX(event)},#{@bottom}V#{@top}") @drawEvent("M#{@transX(event)},#{@bottom}V#{@top}")
# draw y axis labels, horizontal lines # draw y axis labels, horizontal lines
# #
@ -256,14 +255,17 @@ class Morris.Grid extends Morris.EventEmitter
v = parseFloat(lineY.toFixed(@precision)) v = parseFloat(lineY.toFixed(@precision))
y = @transY(v) y = @transY(v)
if @options.axes if @options.axes
@morrisSVG.drawYAxisLabel(@left - @options.padding / 2, y, @yAxisFormat(v)) @drawYAxisLabel(@left - @options.padding / 2, y, @yAxisFormat(v))
if @options.grid if @options.grid
@morrisSVG.drawGridLine("M#{@left},#{y}H#{@left + @width}") @drawGridLine("M#{@left},#{y}H#{@left + @width}")
# @private # @private
# #
measureText: (text, fontSize = 12) -> measureText: (text, fontSize = 12) ->
@morrisSVG.measureText(text, fontSize) tt = @raphael.text(100, 100, text).attr('font-size', fontSize)
ret = tt.getBBox()
tt.remove()
ret
# @private # @private
# #
@ -282,6 +284,27 @@ class Morris.Grid extends Morris.EventEmitter
if hit? if hit?
@hover.update(hit...) @hover.update(hit...)
drawGoal: (path) ->
@raphael.path(path)
.attr('stroke', @options.goalLineColors[i % @options.goalLineColors.length])
.attr('stroke-width', @options.goalStrokeWidth)
drawEvent: (path) ->
@raphael.path(path)
.attr('stroke', @options.eventLineColors[i % @options.eventLineColors.length])
.attr('stroke-width', @options.eventStrokeWidth)
drawYAxisLabel: (xPos, yPos, text) ->
@raphael.text(xPos, yPos, text)
.attr('font-size', @options.gridTextSize)
.attr('fill', @options.gridTextColor)
.attr('text-anchor', 'end')
drawGridLine: (path) ->
@raphael.path(path)
.attr('stroke', @options.gridLineColor)
.attr('stroke-width', @options.gridStrokeWidth)
# Parse a date into a javascript timestamp # Parse a date into a javascript timestamp
# #
# #

View file

@ -138,7 +138,7 @@ class Morris.Line extends Morris.Grid
ypos = @bottom + @options.gridTextSize * 1.25 ypos = @bottom + @options.gridTextSize * 1.25
prevLabelMargin = null prevLabelMargin = null
drawLabel = (labelText, xpos) => drawLabel = (labelText, xpos) =>
label = @morrisSVG.drawXAxisLabel(@transX(xpos), ypos, labelText) label = @drawXAxisLabel(@transX(xpos), ypos, labelText)
labelBox = label.getBBox() labelBox = label.getBBox()
# ensure a minimum of `xLabelMargin` pixels between labels, and ensure # ensure a minimum of `xLabelMargin` pixels between labels, and ensure
# labels don't overflow the container # labels don't overflow the container
@ -168,12 +168,12 @@ class Morris.Line extends Morris.Grid
for i in [@options.ykeys.length-1..0] for i in [@options.ykeys.length-1..0]
path = @paths[i] path = @paths[i]
if path isnt null if path isnt null
@morrisSVG.drawLinePath(path, @colorFor(row, i, 'line')) #row isn't available here? @drawLinePath(path, @colorFor(row, i, 'line')) #row isn't available here?
@seriesPoints = ([] for i in [0...@options.ykeys.length]) @seriesPoints = ([] for i in [0...@options.ykeys.length])
for i in [@options.ykeys.length-1..0] for i in [@options.ykeys.length-1..0]
for row in @data for row in @data
if row._y[i]? if row._y[i]?
circle = @morrisSVG.drawLinePoint(row._x, row._y[i], @options.pointSize, @colorFor(row, i, 'point'), i) circle = @drawLinePoint(row._x, row._y[i], @options.pointSize, @colorFor(row, i, 'point'), i)
else else
circle = null circle = null
@seriesPoints[i].push(circle) @seriesPoints[i].push(circle)
@ -246,6 +246,29 @@ class Morris.Line extends Morris.Grid
else else
@options.lineColors[sidx % @options.lineColors.length] @options.lineColors[sidx % @options.lineColors.length]
drawXAxisLabel: (xPos, yPos, text) ->
@raphael.text(xPos, yPos, text)
.attr('font-size', @options.gridTextSize)
.attr('fill', @options.gridTextColor)
drawLinePath: (path, lineColor) ->
@raphael.path(path)
.attr('stroke', lineColor)
.attr('stroke-width', @options.lineWidth)
drawLinePoint: (xPos, yPos, size, pointColor, lineIndex) ->
@raphael.circle(xPos, yPos, size)
.attr('fill', pointColor)
.attr('stroke-width', @strokeWidthForSeries(lineIndex))
.attr('stroke', @strokeForSeries(lineIndex))
# @private
strokeWidthForSeries: (index) ->
@options.pointWidths[index % @options.pointWidths.length]
# @private
strokeForSeries: (index) ->
@options.pointStrokeColors[index % @options.pointStrokeColors.length]
# generate a series of label, timestamp pairs for x-axis labels # generate a series of label, timestamp pairs for x-axis labels
# #

View file

@ -1,82 +0,0 @@
class Morris.SVG
constructor: (element, options) ->
@raphael = new Raphael(element)
@options = options
clear: ->
@raphael.clear()
drawGoal: (path) ->
@raphael.path(path)
.attr('stroke', @options.goalLineColors[i % @options.goalLineColors.length])
.attr('stroke-width', @options.goalStrokeWidth)
drawEvent: (path) ->
@raphael.path(path)
.attr('stroke', @options.eventLineColors[i % @options.eventLineColors.length])
.attr('stroke-width', @options.eventStrokeWidth)
drawYAxisLabel: (xPos, yPos, text) ->
@raphael.text(xPos, yPos, text)
.attr('font-size', @options.gridTextSize)
.attr('fill', @options.gridTextColor)
.attr('text-anchor', 'end')
drawXAxisLabel: (xPos, yPos, text) ->
label = @raphael.text(xPos, yPos, text)
.attr('font-size', @options.gridTextSize)
.attr('fill', @options.gridTextColor)
drawGridLine: (path) ->
@raphael.path(path)
.attr('stroke', @options.gridLineColor)
.attr('stroke-width', @options.gridStrokeWidth)
measureText: (text, fontSize = 12) ->
tt = @raphael.text(100, 100, text).attr('font-size', fontSize)
ret = tt.getBBox()
tt.remove()
ret
drawEmptyDonutLabel: (xPos, yPos, fontSize, fontWeight) ->
text = @raphael.text(xPos, yPos, '').attr('font-size', fontSize)
text.attr('font-weight', fontWeight) if fontWeight?
return text
drawDonutArc: (path, color) ->
@raphael.path(path).attr(stroke: color, 'stroke-width': 2, opacity: 0)
drawDonutSegment: (path, color, hoverFunction) ->
@raphael.path(path)
.attr(fill: color, stroke: 'white', 'stroke-width': 3)
.hover(hoverFunction)
drawFilledPath: (path, fill) ->
@raphael.path(path)
.attr('fill', fill)
.attr('stroke-width', 0)
drawLinePath: (path, lineColor) ->
@raphael.path(path)
.attr('stroke', lineColor)
.attr('stroke-width', @options.lineWidth)
drawLinePoint: (xPos, yPos, size, pointColor, lineIndex) ->
circle = @raphael.circle(xPos, yPos, size)
.attr('fill', pointColor)
.attr('stroke-width', @strokeWidthForSeries(lineIndex))
.attr('stroke', @strokeForSeries(lineIndex))
drawBar: (xPos, yPos, width, height, barColor) ->
@raphael.rect(xPos, yPos, width, height)
.attr('fill', barColor)
.attr('stroke-width', 0)
# @private
strokeWidthForSeries: (index) ->
@options.pointWidths[index % @options.pointWidths.length]
# @private
strokeForSeries: (index) ->
@options.pointStrokeColors[index % @options.pointStrokeColors.length]

View file

@ -25,10 +25,10 @@ describe 'Morris.Line', ->
pointStrokeColors: [red, blue] pointStrokeColors: [red, blue]
pointWidths: [1, 2] pointWidths: [1, 2]
pointFillColors: [null, red] pointFillColors: [null, red]
chart.morrisSVG.strokeWidthForSeries(0).should.equal 1 chart.strokeWidthForSeries(0).should.equal 1
chart.morrisSVG.strokeForSeries(0).should.equal red chart.strokeForSeries(0).should.equal red
chart.morrisSVG.strokeWidthForSeries(1).should.equal 2 chart.strokeWidthForSeries(1).should.equal 2
chart.morrisSVG.strokeForSeries(1).should.equal blue chart.strokeForSeries(1).should.equal blue
chart.colorFor(chart.data[0], 0, 'point').should.equal chart.colorFor(chart.data[0], 0, 'line') chart.colorFor(chart.data[0], 0, 'point').should.equal chart.colorFor(chart.data[0], 0, 'line')
chart.colorFor(chart.data[1], 1, 'point').should.equal red chart.colorFor(chart.data[1], 1, 'point').should.equal red