mirror of
https://github.com/morrisjs/morris.js.git
synced 2024-11-13 07:11:12 +01:00
encapsulate all interaction with raphael. Reuse some methods. Write tests for all svg structural elements and attributes
This commit is contained in:
parent
7848b49479
commit
14c56165c6
8 changed files with 107 additions and 58 deletions
1
grunt.js
1
grunt.js
|
@ -15,6 +15,7 @@ module.exports = function (grunt) {
|
|||
concat: {
|
||||
'build/morris.coffee': [
|
||||
'lib/morris.coffee',
|
||||
'lib/morris.svg.coffee',
|
||||
'lib/morris.grid.coffee',
|
||||
'lib/morris.hover.coffee',
|
||||
'lib/morris.line.coffee',
|
||||
|
|
|
@ -26,9 +26,7 @@ class Morris.Area extends Morris.Line
|
|||
path = @paths[i]
|
||||
if path isnt null
|
||||
path = path + "L#{@transX(@xmax)},#{@bottom}L#{@transX(@xmin)},#{@bottom}Z"
|
||||
@r.path(path)
|
||||
.attr('fill', @fillForSeries(i))
|
||||
.attr('stroke-width', 0)
|
||||
@morrisSVG.drawFilledPath(path, @fillForSeries(i))
|
||||
super()
|
||||
|
||||
fillForSeries: (i) ->
|
||||
|
|
|
@ -59,9 +59,7 @@ class Morris.Bar extends Morris.Grid
|
|||
prevLabelMargin = null
|
||||
for i in [0...@data.length]
|
||||
row = @data[@data.length - 1 - i]
|
||||
label = @r.text(row._x, ypos, row.label)
|
||||
.attr('font-size', @options.gridTextSize)
|
||||
.attr('fill', @options.gridTextColor)
|
||||
label = @morrisSVG.drawXAxisLabel(row._x, ypos, row.label)
|
||||
labelBox = label.getBBox()
|
||||
# ensure a minimum of `xLabelMargin` pixels between labels, and ensure
|
||||
# labels don't overflow the container
|
||||
|
@ -96,9 +94,7 @@ class Morris.Bar extends Morris.Grid
|
|||
size = bottom - top
|
||||
|
||||
top -= lastTop if @options.stacked
|
||||
@r.rect(left, top, barWidth, size)
|
||||
.attr('fill', @colorFor(row, sidx, 'bar'))
|
||||
.attr('stroke-width', 0)
|
||||
@morrisSVG.drawBar(left, top, barWidth, size, @colorFor(row, sidx, 'bar'))
|
||||
|
||||
lastTop += size
|
||||
else
|
||||
|
|
|
@ -54,7 +54,7 @@ class Morris.Donut
|
|||
redraw: ->
|
||||
@el.empty()
|
||||
|
||||
@r = new Raphael(@el[0])
|
||||
@morrisSVG = new Morris.SVG(@el[0], @options)
|
||||
|
||||
cx = @el.width() / 2
|
||||
cy = @el.height() / 2
|
||||
|
@ -72,13 +72,13 @@ class Morris.Donut
|
|||
for d in @data
|
||||
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.render @r
|
||||
seg.render @morrisSVG
|
||||
@segments.push seg
|
||||
seg.on 'hover', @select
|
||||
last = next
|
||||
idx += 1
|
||||
@text1 = @r.text(cx, cy - 10, '').attr('font-size': 15, 'font-weight': 800)
|
||||
@text2 = @r.text(cx, cy + 10, '').attr('font-size': 14)
|
||||
@text1 = @morrisSVG.drawEmptyDonutLabel(cx, cy - 10, 15, 800)
|
||||
@text2 = @morrisSVG.drawEmptyDonutLabel(cx, cy + 10, 14)
|
||||
max_value = Math.max.apply(null, d.value for d in @data)
|
||||
idx = 0
|
||||
for d in @data
|
||||
|
@ -147,11 +147,9 @@ class Morris.DonutSegment extends Morris.EventEmitter
|
|||
"M#{ix0},#{iy0}" +
|
||||
"A#{r},#{r},0,#{@long},0,#{ix1},#{iy1}")
|
||||
|
||||
render: (raphael) ->
|
||||
@arc = raphael.path(@hilight).attr(stroke: @color, 'stroke-width': 2, opacity: 0)
|
||||
@seg = raphael.path(@path)
|
||||
.attr(fill: @color, stroke: 'white', 'stroke-width': 3)
|
||||
.hover(=> @fire('hover', @))
|
||||
render: (morrisSVG) ->
|
||||
@arc = morrisSVG.drawDonutArc(@hilight, @color)
|
||||
@seg = morrisSVG.drawDonutSegment(@path, @color, => @fire('hover', @))
|
||||
|
||||
select: =>
|
||||
unless @selected
|
||||
|
|
|
@ -27,6 +27,7 @@ class Morris.Grid extends Morris.EventEmitter
|
|||
|
||||
# the raphael drawing instance
|
||||
@r = new Raphael(@el[0])
|
||||
@morrisSVG = new Morris.SVG(@el[0], @options)
|
||||
|
||||
# some redraw stuff
|
||||
@elementWidth = null
|
||||
|
@ -227,7 +228,7 @@ class Morris.Grid extends Morris.EventEmitter
|
|||
# If you need to re-size your charts, call this method after changing the
|
||||
# size of the container element.
|
||||
redraw: ->
|
||||
@r.clear()
|
||||
@morrisSVG.clear()
|
||||
@_calc()
|
||||
@drawGrid()
|
||||
@drawGoals()
|
||||
|
@ -238,16 +239,12 @@ class Morris.Grid extends Morris.EventEmitter
|
|||
#
|
||||
drawGoals: ->
|
||||
for goal, i in @options.goals
|
||||
@r.path("M#{@left},#{@transY(goal)}H#{@left + @width}")
|
||||
.attr('stroke', @options.goalLineColors[i % @options.goalLineColors.length])
|
||||
.attr('stroke-width', @options.goalStrokeWidth)
|
||||
@morrisSVG.drawGoal("M#{@left},#{@transY(goal)}H#{@left + @width}")
|
||||
|
||||
# draw events vertical lines
|
||||
drawEvents: ->
|
||||
for event, i in @events
|
||||
@r.path("M#{@transX(event)},#{@bottom}V#{@top}")
|
||||
.attr('stroke', @options.eventLineColors[i % @options.eventLineColors.length])
|
||||
.attr('stroke-width', @options.eventStrokeWidth)
|
||||
@morrisSVG.drawEvent("M#{@transX(event)},#{@bottom}V#{@top}")
|
||||
|
||||
# draw y axis labels, horizontal lines
|
||||
#
|
||||
|
@ -259,22 +256,14 @@ class Morris.Grid extends Morris.EventEmitter
|
|||
v = parseFloat(lineY.toFixed(@precision))
|
||||
y = @transY(v)
|
||||
if @options.axes
|
||||
@r.text(@left - @options.padding / 2, y, @yAxisFormat(v))
|
||||
.attr('font-size', @options.gridTextSize)
|
||||
.attr('fill', @options.gridTextColor)
|
||||
.attr('text-anchor', 'end')
|
||||
@morrisSVG.drawYAxisLabel(@left - @options.padding / 2, y, @yAxisFormat(v))
|
||||
if @options.grid
|
||||
@r.path("M#{@left},#{y}H#{@left + @width}")
|
||||
.attr('stroke', @options.gridLineColor)
|
||||
.attr('stroke-width', @options.gridStrokeWidth)
|
||||
@morrisSVG.drawGridLine("M#{@left},#{y}H#{@left + @width}")
|
||||
|
||||
# @private
|
||||
#
|
||||
measureText: (text, fontSize = 12) ->
|
||||
tt = @r.text(100, 100, text).attr('font-size', fontSize)
|
||||
ret = tt.getBBox()
|
||||
tt.remove()
|
||||
ret
|
||||
@morrisSVG.measureText(text, fontSize)
|
||||
|
||||
# @private
|
||||
#
|
||||
|
|
|
@ -138,9 +138,7 @@ class Morris.Line extends Morris.Grid
|
|||
ypos = @bottom + @options.gridTextSize * 1.25
|
||||
prevLabelMargin = null
|
||||
drawLabel = (labelText, xpos) =>
|
||||
label = @r.text(@transX(xpos), ypos, labelText)
|
||||
.attr('font-size', @options.gridTextSize)
|
||||
.attr('fill', @options.gridTextColor)
|
||||
label = @morrisSVG.drawXAxisLabel(@transX(xpos), ypos, labelText)
|
||||
labelBox = label.getBBox()
|
||||
# ensure a minimum of `xLabelMargin` pixels between labels, and ensure
|
||||
# labels don't overflow the container
|
||||
|
@ -170,17 +168,12 @@ class Morris.Line extends Morris.Grid
|
|||
for i in [@options.ykeys.length-1..0]
|
||||
path = @paths[i]
|
||||
if path isnt null
|
||||
@r.path(path)
|
||||
.attr('stroke', @colorFor(row, i, 'line'))
|
||||
.attr('stroke-width', @options.lineWidth)
|
||||
@morrisSVG.drawLinePath(path, @colorFor(row, i, 'line')) #row isn't available here?
|
||||
@seriesPoints = ([] for i in [0...@options.ykeys.length])
|
||||
for i in [@options.ykeys.length-1..0]
|
||||
for row in @data
|
||||
if row._y[i]?
|
||||
circle = @r.circle(row._x, row._y[i], @options.pointSize)
|
||||
.attr('fill', @colorFor(row, i, 'point'))
|
||||
.attr('stroke-width', @strokeWidthForSeries(i))
|
||||
.attr('stroke', @strokeForSeries(i))
|
||||
circle = @morrisSVG.drawLinePoint(row._x, row._y[i], @options.pointSize, @colorFor(row, i, 'point'), i)
|
||||
else
|
||||
circle = null
|
||||
@seriesPoints[i].push(circle)
|
||||
|
@ -245,14 +238,6 @@ class Morris.Line extends Morris.Grid
|
|||
@seriesPoints[i][index].animate @pointGrow
|
||||
@prevHilight = index
|
||||
|
||||
# @private
|
||||
strokeWidthForSeries: (index) ->
|
||||
@options.pointWidths[index % @options.pointWidths.length]
|
||||
|
||||
# @private
|
||||
strokeForSeries: (index) ->
|
||||
@options.pointStrokeColors[index % @options.pointStrokeColors.length]
|
||||
|
||||
colorFor: (row, sidx, type) ->
|
||||
if typeof @options.lineColors is 'function'
|
||||
@options.lineColors.call(@, row, sidx, type)
|
||||
|
|
82
lib/morris.svg.coffee
Normal file
82
lib/morris.svg.coffee
Normal file
|
@ -0,0 +1,82 @@
|
|||
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]
|
|
@ -25,10 +25,10 @@ describe 'Morris.Line', ->
|
|||
pointStrokeColors: [red, blue]
|
||||
pointWidths: [1, 2]
|
||||
pointFillColors: [null, red]
|
||||
chart.strokeWidthForSeries(0).should.equal 1
|
||||
chart.strokeForSeries(0).should.equal red
|
||||
chart.strokeWidthForSeries(1).should.equal 2
|
||||
chart.strokeForSeries(1).should.equal blue
|
||||
chart.morrisSVG.strokeWidthForSeries(0).should.equal 1
|
||||
chart.morrisSVG.strokeForSeries(0).should.equal red
|
||||
chart.morrisSVG.strokeWidthForSeries(1).should.equal 2
|
||||
chart.morrisSVG.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[1], 1, 'point').should.equal red
|
||||
|
||||
|
|
Loading…
Reference in a new issue