Angled labels.

This commit is contained in:
Olly Smith 2013-05-09 22:15:05 +01:00
parent c170062eae
commit 05cdc7f3e4
7 changed files with 97 additions and 67 deletions

View File

@ -32,7 +32,7 @@ Morris.Bar({
xkey: 'period',
ykeys: ['licensed', 'sorned'],
labels: ['Licensed', 'SORN'],
xLabelsDiagonal: true
xLabelAngle: 60
});
</pre>
</body>

View File

@ -15,7 +15,7 @@
<pre id="code" class="prettyprint linenums">
/* data stolen from http://howmanyleft.co.uk/vehicle/jaguar_'e'_type */
var day_data = [
{"period": "2012-10-01", "licensed": 3407, "sorned": 660},
{"period": "2012-10-30", "licensed": 3407, "sorned": 660},
{"period": "2012-09-30", "licensed": 3351, "sorned": 629},
{"period": "2012-09-29", "licensed": 3269, "sorned": 618},
{"period": "2012-09-20", "licensed": 3246, "sorned": 661},
@ -32,7 +32,7 @@ Morris.Line({
xkey: 'period',
ykeys: ['licensed', 'sorned'],
labels: ['Licensed', 'SORN'],
xLabelsDiagonal: true
xLabelAngle: 60
});
</pre>
</body>

View File

@ -56,22 +56,32 @@ class Morris.Bar extends Morris.Grid
# @private
drawXAxis: ->
# draw x axis labels
ypos = @bottom + @options.gridTextSize * 1.25
ypos = @bottom + @options.padding / 2
prevLabelMargin = null
prevAngleMargin = null
for i in [0...@data.length]
row = @data[@data.length - 1 - i]
label = @drawXAxisLabel(row._x, ypos, row.label)
textBox = label.getBBox()
label.transform("r#{-@options.xLabelAngle}")
labelBox = label.getBBox()
if @options.xLabelsDiagonal
label.rotate(-90).translate(-labelBox.width/2, 0)
label.transform("t0,#{labelBox.height / 2}...")
if @options.xLabelAngle != 0
offset = -0.5 * textBox.width *
Math.cos(@options.xLabelAngle * Math.PI / 180.0)
label.transform("t#{offset},0...")
# try to avoid overlaps
if (not prevLabelMargin? or
prevLabelMargin >= labelBox.x + labelBox.width or
prevAngleMargin? and prevAngleMargin >= labelBox.x) and
labelBox.x >= 0 and (labelBox.x + labelBox.width) < @el.width()
if @options.xLabelAngle != 0
margin = 1.25 * @options.gridTextSize /
Math.sin(@options.xLabelAngle * Math.PI / 180.0)
prevAngleMargin = labelBox.x - margin
prevLabelMargin = labelBox.x - @options.xLabelMargin
else
# ensure a minimum of `xLabelMargin` pixels between labels, and ensure
# labels don't overflow the container
if (not prevLabelMargin? or prevLabelMargin >= labelBox.x + labelBox.width) and
labelBox.x >= 0 and (labelBox.x + labelBox.width) < @el.width()
prevLabelMargin = labelBox.x - @options.xLabelMargin
else
label.remove()
label.remove()
# draw the data series
#

View File

@ -67,7 +67,7 @@ class Morris.Grid extends Morris.EventEmitter
gridTextSize: 12
hideHover: false
yLabelFormat: null
xLabelsDiagonal: false
xLabelAngle: 0
numLines: 5
padding: 25
parseTime: true
@ -241,12 +241,9 @@ class Morris.Grid extends Morris.EventEmitter
yLabelWidths = for gridLine in @grid
@measureText(@yAxisFormat(gridLine), @options.gridTextSize).width
@left += Math.max(yLabelWidths...)
if @options.xLabelsDiagonal
bottomOffsets = for i in [0...@data.length]
@measureText(@data[i].text, @options.gridTextSize, -90).height
@bottom -= Math.max(bottomOffsets...)
else
@bottom -= 1.5 * @options.gridTextSize
bottomOffsets = for i in [0...@data.length]
@measureText(@data[i].text, @options.gridTextSize, -@options.xLabelAngle).height
@bottom -= Math.max(bottomOffsets...)
@width = Math.max(1, @right - @left)
@height = Math.max(1, @bottom - @top)
@dx = @width / (@xmax - @xmin)

View File

@ -36,7 +36,7 @@ class Morris.Line extends Morris.Grid
smooth: true
xLabels: 'auto'
xLabelFormat: null
xLabelMargin: 50
xLabelMargin: 24
continuousLine: true
hideHover: false
@ -143,21 +143,32 @@ class Morris.Line extends Morris.Grid
# @private
drawXAxis: ->
# draw x axis labels
ypos = @bottom + @options.gridTextSize * 1.25
ypos = @bottom + @options.padding / 2
prevLabelMargin = null
prevAngleMargin = null
drawLabel = (labelText, xpos) =>
label = @drawXAxisLabel(@transX(xpos), ypos, labelText)
textBox = label.getBBox()
label.transform("r#{-@options.xLabelAngle}")
labelBox = label.getBBox()
if @options.xLabelsDiagonal
label.rotate(-90).translate(-labelBox.height/2, 0)
label.transform("t0,#{labelBox.height / 2}...")
if @options.xLabelAngle != 0
offset = -0.5 * textBox.width *
Math.cos(@options.xLabelAngle * Math.PI / 180.0)
label.transform("t#{offset},0...")
# try to avoid overlaps
labelBox = label.getBBox()
if (not prevLabelMargin? or
prevLabelMargin >= labelBox.x + labelBox.width or
prevAngleMargin? and prevAngleMargin >= labelBox.x) and
labelBox.x >= 0 and (labelBox.x + labelBox.width) < @el.width()
if @options.xLabelAngle != 0
margin = 1.25 * @options.gridTextSize /
Math.sin(@options.xLabelAngle * Math.PI / 180.0)
prevAngleMargin = labelBox.x - margin
prevLabelMargin = labelBox.x - @options.xLabelMargin
else
# ensure a minimum of `xLabelMargin` pixels between labels, and ensure
# labels don't overflow the container
if (not prevLabelMargin? or prevLabelMargin >= labelBox.x + labelBox.width) and
labelBox.x >= 0 and (labelBox.x + labelBox.width) < @el.width()
prevLabelMargin = labelBox.x - @options.xLabelMargin
else
label.remove()
label.remove()
if @options.parseTime
if @data.length == 1 and @options.xLabels == 'auto'
# where there's only one value in the series, we can't make a

View File

@ -127,7 +127,7 @@
gridTextSize: 12,
hideHover: false,
yLabelFormat: null,
xLabelsDiagonal: false,
xLabelAngle: 0,
numLines: 5,
padding: 25,
parseTime: true,
@ -368,20 +368,15 @@
return _results;
}).call(this);
this.left += Math.max.apply(Math, yLabelWidths);
if (this.options.xLabelsDiagonal) {
bottomOffsets = (function() {
var _i, _ref, _results;
_results = [];
for (i = _i = 0, _ref = this.data.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
_results.push(this.measureText(this.data[i].text, this.options.gridTextSize, -90).height);
}
return _results;
}).call(this);
this.maxXLabelHeight = Math.max.apply(Math, bottomOffsets);
this.bottom -= this.maxXLabelHeight;
} else {
this.bottom -= 1.5 * this.options.gridTextSize;
}
bottomOffsets = (function() {
var _i, _ref, _results;
_results = [];
for (i = _i = 0, _ref = this.data.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
_results.push(this.measureText(this.data[i].text, this.options.gridTextSize, -this.options.xLabelAngle).height);
}
return _results;
}).call(this);
this.bottom -= Math.max.apply(Math, bottomOffsets);
}
this.width = Math.max(1, this.right - this.left);
this.height = Math.max(1, this.bottom - this.top);
@ -680,7 +675,7 @@
smooth: true,
xLabels: 'auto',
xLabelFormat: null,
xLabelMargin: 50,
xLabelMargin: 24,
continuousLine: true,
hideHover: false
};
@ -842,22 +837,31 @@
};
Line.prototype.drawXAxis = function() {
var drawLabel, l, labels, prevLabelMargin, row, ypos, _i, _len, _results,
var drawLabel, l, labels, prevAngleMargin, prevLabelMargin, row, ypos, _i, _len, _results,
_this = this;
ypos = this.bottom + this.options.gridTextSize * 1.25;
ypos = this.bottom + this.options.padding / 2;
prevLabelMargin = null;
prevAngleMargin = null;
drawLabel = function(labelText, xpos) {
var label, labelBox;
var label, labelBox, margin, offset, textBox;
label = _this.drawXAxisLabel(_this.transX(xpos), ypos, labelText);
textBox = label.getBBox();
label.transform("r" + (-_this.options.xLabelAngle));
labelBox = label.getBBox();
if (_this.options.xLabelsDiagonal) {
return label.rotate(-90).translate(-labelBox.height / 2, 0);
} else {
if ((!(prevLabelMargin != null) || prevLabelMargin >= labelBox.x + labelBox.width) && labelBox.x >= 0 && (labelBox.x + labelBox.width) < _this.el.width()) {
return prevLabelMargin = labelBox.x - _this.options.xLabelMargin;
} else {
return label.remove();
label.transform("t0," + (labelBox.height / 2) + "...");
if (_this.options.xLabelAngle !== 0) {
offset = -0.5 * textBox.width * Math.cos(_this.options.xLabelAngle * Math.PI / 180.0);
label.transform("t" + offset + ",0...");
}
labelBox = label.getBBox();
if ((!(prevLabelMargin != null) || prevLabelMargin >= labelBox.x + labelBox.width || (prevAngleMargin != null) && prevAngleMargin >= labelBox.x) && labelBox.x >= 0 && (labelBox.x + labelBox.width) < _this.el.width()) {
if (_this.options.xLabelAngle !== 0) {
margin = 1.25 * _this.options.gridTextSize / Math.sin(_this.options.xLabelAngle * Math.PI / 180.0);
prevAngleMargin = labelBox.x - margin;
}
return prevLabelMargin = labelBox.x - _this.options.xLabelMargin;
} else {
return label.remove();
}
};
if (this.options.parseTime) {
@ -1351,22 +1355,30 @@
};
Bar.prototype.drawXAxis = function() {
var i, label, labelBox, prevLabelMargin, row, ypos, _i, _ref, _results;
ypos = this.bottom + this.options.gridTextSize * 1.25;
var i, label, labelBox, margin, offset, prevAngleMargin, prevLabelMargin, row, textBox, ypos, _i, _ref, _results;
ypos = this.bottom + this.options.padding / 2;
prevLabelMargin = null;
prevAngleMargin = null;
_results = [];
for (i = _i = 0, _ref = this.data.length; 0 <= _ref ? _i < _ref : _i > _ref; i = 0 <= _ref ? ++_i : --_i) {
row = this.data[this.data.length - 1 - i];
label = this.drawXAxisLabel(row._x, ypos, row.label);
textBox = label.getBBox();
label.transform("r" + (-this.options.xLabelAngle));
labelBox = label.getBBox();
if (this.options.xLabelsDiagonal) {
_results.push(label.rotate(-90).translate(-labelBox.width / 2, 0));
} else {
if ((!(prevLabelMargin != null) || prevLabelMargin >= labelBox.x + labelBox.width) && labelBox.x >= 0 && (labelBox.x + labelBox.width) < this.el.width()) {
_results.push(prevLabelMargin = labelBox.x - this.options.xLabelMargin);
} else {
_results.push(label.remove());
label.transform("t0," + (labelBox.height / 2) + "...");
if (this.options.xLabelAngle !== 0) {
offset = -0.5 * textBox.width * Math.cos(this.options.xLabelAngle * Math.PI / 180.0);
label.transform("t" + offset + ",0...");
}
if ((!(prevLabelMargin != null) || prevLabelMargin >= labelBox.x + labelBox.width || (prevAngleMargin != null) && prevAngleMargin >= labelBox.x) && labelBox.x >= 0 && (labelBox.x + labelBox.width) < this.el.width()) {
if (this.options.xLabelAngle !== 0) {
margin = 1.25 * this.options.gridTextSize / Math.sin(this.options.xLabelAngle * Math.PI / 180.0);
prevAngleMargin = labelBox.x - margin;
}
_results.push(prevLabelMargin = labelBox.x - this.options.xLabelMargin);
} else {
_results.push(label.remove());
}
}
return _results;

2
morris.min.js vendored

File diff suppressed because one or more lines are too long