I'm trying to get a logarithmic scale for the y-axis of a morris.js line chart.
http://www.oesmith.co.uk/morris.js/lines.html
I already tried playing with the yLabelFormat option, but it's not what I need. Any hint is appreciated.
If there is no way of doing this with morris.js, you can suggest another lightweight javascript library to make simple line charts with logarithmic scale.
You can extend Morris and modify the transY
function to do the logarithmic scale.
I also added the gridIntegers
parameter to have only integers on the y-Axis.
Remove the code after the transY
function if you want only the yLogScale
.
Use the following snippet to view the result with yLogScale
parameter set as true
or false
.
(function () {
var $, MyMorris;
MyMorris = window.MyMorris = {};
$ = jQuery;
MyMorris = Object.create(Morris);
MyMorris.Grid.prototype.gridDefaults["yLogScale"] = false;
MyMorris.Grid.prototype.gridDefaults["gridIntegers"] = false;
MyMorris.Grid.prototype.transY = function (y) {
if (!this.options.horizontal) {
if (this.options.yLogScale) {
return this.bottom - (this.height * Math.log((y + 1) - this.ymin) / Math.log(this.ymax / (this.ymin + 1)));
} else {
return this.bottom - (y - this.ymin) * this.dy;
}
} else {
return this.left + (y - this.ymin) * this.dy;
}
};
MyMorris.Grid.prototype.setData = function (data, redraw) {
var e, idx, index, maxGoal, minGoal, ret, row, step, total, y, ykey, ymax, ymin, yval, _ref;
if (redraw == null) {
redraw = true;
}
this.options.data = data;
if ((data == null) || data.length === 0) {
this.data = [];
this.raphael.clear();
if (this.hover != null) {
this.hover.hide();
}
return;
}
ymax = this.cumulative ? 0 : null;
ymin = this.cumulative ? 0 : null;
if (this.options.goals.length > 0) {
minGoal = Math.min.apply(Math, this.options.goals);
maxGoal = Math.max.apply(Math, this.options.goals);
ymin = ymin != null ? Math.min(ymin, minGoal) : minGoal;
ymax = ymax != null ? Math.max(ymax, maxGoal) : maxGoal;
}
this.data = (function () {
var _i, _len, _results;
_results = [];
for (index = _i = 0, _len = data.length; _i < _len; index = ++_i) {
row = data[index];
ret = {
src: row
};
ret.label = row[this.options.xkey];
if (this.options.parseTime) {
ret.x = Morris.parseDate(ret.label);
if (this.options.dateFormat) {
ret.label = this.options.dateFormat(ret.x);
} else if (typeof ret.label === 'number') {
ret.label = new Date(ret.label).toString();
}
} else {
ret.x = index;
if (this.options.xLabelFormat) {
ret.label = this.options.xLabelFormat(ret);
}
}
total = 0;
ret.y = (function () {
var _j, _len1, _ref, _results1;
_ref = this.options.ykeys;
_results1 = [];
for (idx = _j = 0, _len1 = _ref.length; _j < _len1; idx = ++_j) {
ykey = _ref[idx];
yval = row[ykey];
if (typeof yval === 'string') {
yval = parseFloat(yval);
}
if ((yval != null) && typeof yval !== 'number') {
yval = null;
}
if (yval != null) {
if (this.cumulative) {
total += yval;
} else {
if (ymax != null) {
ymax = Math.max(yval, ymax);
ymin = Math.min(yval, ymin);
} else {
ymax = ymin = yval;
}
}
}
if (this.cumulative && (total != null)) {
ymax = Math.max(total, ymax);
ymin = Math.min(total, ymin);
}
_results1.push(yval);
}
return _results1;
}).call(this);
_results.push(ret);
}
return _results;
}).call(this);
if (this.options.parseTime) {
this.data = this.data.sort(function (a, b) {
return (a.x > b.x) - (b.x > a.x);
});
}
this.xmin = this.data[0].x;
this.xmax = this.data[this.data.length - 1].x;
this.events = [];
if (this.options.events.length > 0) {
if (this.options.parseTime) {
this.events = (function () {
var _i, _len, _ref, _results;
_ref = this.options.events;
_results = [];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
e = _ref[_i];
_results.push(Morris.parseDate(e));
}
return _results;
}).call(this);
} else {
this.events = this.options.events;
}
this.xmax = Math.max(this.xmax, Math.max.apply(Math, this.events));
this.xmin = Math.min(this.xmin, Math.min.apply(Math, this.events));
}
if (this.xmin === this.xmax) {
this.xmin -= 1;
this.xmax += 1;
}
this.ymin = this.yboundary('min', ymin);
this.ymax = this.yboundary('max', ymax);
if (this.ymin === this.ymax) {
if (ymin) {
this.ymin -= 1;
}
this.ymax += 1;
}
if (((_ref = this.options.axes) === true || _ref === 'both' || _ref === 'y') || this.options.grid === true) {
if (this.options.ymax === this.gridDefaults.ymax && this.options.ymin === this.gridDefaults.ymin) {
this.grid = this.autoGridLines(this.ymin, this.ymax, this.options.numLines);
this.ymin = Math.min(this.ymin, this.grid[0]);
this.ymax = Math.max(this.ymax, this.grid[this.grid.length - 1]);
} else {
step = (this.ymax - this.ymin) / (this.options.numLines - 1);
if (this.options.gridIntegers) {
step = Math.max(1, Math.round(step));
}
this.grid = (function () {
var _i, _ref1, _ref2, _results;
_results = [];
for (y = _i = _ref1 = this.ymin, _ref2 = this.ymax; step > 0 ? _i <= _ref2 : _i >= _ref2; y = _i += step) {
_results.push(y);
}
return _results;
}).call(this);
}
}
this.dirty = true;
if (redraw) {
return this.redraw();
}
};
}).call(this);
var data = [
{ y: '2016-01', a: 1, b: 5 },
{ y: '2016-02', a: 2, b: 3 },
{ y: '2016-03', a: 2, b: 2 },
{ y: '2016-04', a: 1, b: 1000 },
{ y: '2016-05', a: 2, b: 2 },
{ y: '2016-06', a: 3, b: 3 },
{ y: '2016-07', a: 1, b: 2 }
];
var morrisArea =
Morris.Area({
element: 'chart',
data: data,
xkey: 'y',
ykeys: ['a', 'b'],
labels: ['Label A', 'Label B'],
fillOpacity: 0.6,
hideHover: 'auto',
resize: true,
yLogScale: true,
gridIntegers: true,
pointFillColors: ['#ffffff'],
pointStrokeColors: ['black'],
lineColors: ['gray', 'blue'],
gridIntegers: true,
ymin: 0
});
$(".button").on("click", function() {
$(".button").removeClass("on");
$(this).addClass("on");
});
function setYLogScale(status) {
morrisArea.options["yLogScale"] = status;
morrisArea.setData(data);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js"></script>
<link href="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.css" rel="stylesheet" />
<style>
body { font-family: Arial; }
.button {
padding: 3px 5px;
border: 1px solid black;
background-color: #eeeeee;
display: inline-block;
cursor: pointer;
}
.on { background-color: lightblue; }
</style>
<div class="button" onclick="setYLogScale(true);">yLogScale ON</div>
<div class="button" onclick="setYLogScale(false);">yLogScale OFF</div>
<div id="chart"></div>
来源:https://stackoverflow.com/questions/24328631/logarithmic-y-axis-with-morris-js