light-chart
Version:
Charts for mobile visualization.
341 lines (309 loc) • 9.2 kB
JavaScript
const Util = require('../../util/common');
const Axis = require('../../component/axis/');
const Global = require('../../global');
const { Shape } = require('../../graphic/index');
function formatTicks(ticks) {
const tmp = ticks.slice(0);
if (tmp.length > 0) {
const first = tmp[0];
const last = tmp[tmp.length - 1];
if (first.value !== 0) {
tmp.unshift({
value: 0
});
}
if (last.value !== 1) {
tmp.push({
value: 1
});
}
}
return tmp;
}
class AxisController {
constructor(cfg) {
this.axisCfg = {};
this.frontPlot = null;
this.backPlot = null;
this.axes = {}; // store the axes's options
Util.mix(this, cfg);
}
_isHide(field) {
const axisCfg = this.axisCfg;
return !axisCfg || axisCfg[field] === false;
}
_getLinePosition(scale, dimType, index, transposed) {
let position = '';
const field = scale.field;
const axisCfg = this.axisCfg;
if (axisCfg[field] && axisCfg[field].position) {
position = axisCfg[field].position;
} else if (dimType === 'x') {
position = transposed ? 'left' : 'bottom';
} else if (dimType === 'y') {
position = index ? 'right' : 'left';
if (transposed) {
position = 'bottom';
}
}
return position;
}
_getLineCfg(coord, dimType, position) {
let start;
let end;
let factor = 1; // Mark clockwise or counterclockwise
if (dimType === 'x') {
start = {
x: 0,
y: 0
};
end = {
x: 1,
y: 0
};
} else {
if (position === 'right') { // there will be several y axes
start = {
x: 1,
y: 0
};
end = {
x: 1,
y: 1
};
} else {
start = {
x: 0,
y: 0
};
end = {
x: 0,
y: 1
};
factor = -1;
}
}
if (coord.transposed) {
factor *= -1;
}
return {
offsetFactor: factor,
start: coord.convertPoint(start),
end: coord.convertPoint(end)
};
}
_getCircleCfg(coord) {
return {
startAngle: coord.startAngle,
endAngle: coord.endAngle,
center: coord.center,
radius: coord.circleRadius
};
}
_getRadiusCfg(coord) {
const transposed = coord.transposed;
let start;
let end;
if (transposed) {
start = { x: 0, y: 0 };
end = { x: 1, y: 0 };
} else {
start = { x: 0, y: 0 };
end = { x: 0, y: 1 };
}
return {
offsetFactor: -1,
start: coord.convertPoint(start),
end: coord.convertPoint(end)
};
}
_getAxisCfg(coord, scale, verticalScale, dimType, defaultCfg) {
const self = this;
const axisCfg = this.axisCfg;
const ticks = scale.getTicks();
const cfg = Util.deepMix({
ticks,
frontContainer: this.frontPlot,
backContainer: this.backPlot
}, defaultCfg, axisCfg[scale.field]);
const labels = [];
const label = cfg.label;
const count = ticks.length;
let maxWidth = 0;
let maxHeight = 0;
let labelCfg = label;
Util.each(ticks, (tick, index) => {
if (Util.isFunction(label)) {
const executedLabel = label(tick.text, index, count);
labelCfg = executedLabel ? Util.mix({}, Global._defaultAxis.label, executedLabel) : null;
}
if (labelCfg) {
const textStyle = {};
if (labelCfg.textAlign) {
textStyle.textAlign = labelCfg.textAlign;
}
if (labelCfg.textBaseline) {
textStyle.textBaseline = labelCfg.textBaseline;
}
const axisLabel = new Shape.Text({
className: 'axis-label',
attrs: Util.mix({
x: 0,
y: 0,
text: tick.text,
fontFamily: self.chart.get('canvas').get('fontFamily')
}, labelCfg),
value: tick.value,
textStyle,
top: labelCfg.top,
context: self.chart.get('canvas').get('context')
});
labels.push(axisLabel);
const { width, height } = axisLabel.getBBox();
maxWidth = Math.max(maxWidth, width);
maxHeight = Math.max(maxHeight, height);
}
});
cfg.labels = labels;
cfg.maxWidth = maxWidth;
cfg.maxHeight = maxHeight;
return cfg;
}
_createAxis(coord, scale, verticalScale, dimType, index = '') {
const self = this;
const coordType = coord.type;
const transposed = coord.transposed;
let type;
let key;
let defaultCfg;
if (coordType === 'cartesian' || coordType === 'rect') {
const position = self._getLinePosition(scale, dimType, index, transposed);
defaultCfg = Global.axis[position];
defaultCfg.position = position;
type = 'Line';
key = position;
} else {
if ((dimType === 'x' && !transposed) || (dimType === 'y' && transposed)) {
defaultCfg = Global.axis.circle;
type = 'Circle';
key = 'circle';
} else {
defaultCfg = Global.axis.radius;
type = 'Line';
key = 'radius';
}
}
const cfg = self._getAxisCfg(coord, scale, verticalScale, dimType, defaultCfg);
cfg.type = type;
cfg.dimType = dimType;
cfg.verticalScale = verticalScale;
cfg.index = index;
this.axes[key] = cfg;
}
createAxis(coord, xScale, yScales) {
const self = this;
if (xScale && !self._isHide(xScale.field)) {
self._createAxis(coord, xScale, yScales[0], 'x');
}
Util.each(yScales, function(yScale, index) {
if (!self._isHide(yScale.field)) {
self._createAxis(coord, yScale, xScale, 'y', index);
}
});
const axes = this.axes;
const chart = self.chart;
if (chart._isAutoPadding()) {
const userPadding = Util.parsePadding(chart.get('padding'));
const appendPadding = Util.parsePadding(chart.get('appendPadding'));
const legendRange = chart.get('legendRange') || {
top: 0,
right: 0,
bottom: 0,
left: 0
};
const padding = [
userPadding[0] === 'auto' ? legendRange.top + appendPadding[0] * 2 : userPadding[0],
userPadding[1] === 'auto' ? legendRange.right + appendPadding[1] : userPadding[1],
userPadding[2] === 'auto' ? legendRange.bottom + appendPadding[2] : userPadding[2],
userPadding[3] === 'auto' ? legendRange.left + appendPadding[3] : userPadding[3]
];
if (coord.isPolar) {
const circleAxis = axes.circle;
if (circleAxis) {
const { maxHeight, maxWidth, labelOffset } = circleAxis;
padding[0] += maxHeight + labelOffset;
padding[1] += maxWidth + labelOffset;
padding[2] += maxHeight + labelOffset;
padding[3] += maxWidth + labelOffset;
}
} else {
if (axes.right && userPadding[1] === 'auto') {
const { maxWidth, labelOffset } = axes.right;
padding[1] += maxWidth + labelOffset;
}
if (axes.left && userPadding[3] === 'auto') {
const { maxWidth, labelOffset } = axes.left;
padding[3] += maxWidth + labelOffset;
}
if (axes.bottom && userPadding[2] === 'auto') {
const { maxHeight, labelOffset } = axes.bottom;
padding[2] += maxHeight + labelOffset;
}
}
chart.set('_padding', padding);
chart._updateLayout(padding);
}
Util.each(axes, axis => {
const { type, grid, verticalScale, ticks, dimType, position, index } = axis;
let appendCfg;
if (coord.isPolar) {
if (type === 'Line') {
appendCfg = self._getRadiusCfg(coord);
} else if (type === 'Circle') {
appendCfg = self._getCircleCfg(coord);
}
} else {
appendCfg = self._getLineCfg(coord, dimType, position);
}
if (grid && verticalScale) {
const gridPoints = [];
const verticalTicks = formatTicks(verticalScale.getTicks());
Util.each(ticks, tick => {
const subPoints = [];
Util.each(verticalTicks, verticalTick => {
const x = dimType === 'x' ? tick.value : verticalTick.value;
const y = dimType === 'x' ? verticalTick.value : tick.value;
if (x >= 0 && x <= 1 && y >= 0 && y <= 1) {
const point = coord.convertPoint({
x,
y
});
subPoints.push(point);
}
});
gridPoints.push({
points: subPoints,
_id: 'axis-' + dimType + index + '-grid-' + tick.tickValue
});
});
axis.gridPoints = gridPoints;
if (coord.isPolar) {
axis.center = coord.center;
axis.startAngle = coord.startAngle;
axis.endAngle = coord.endAngle;
}
}
appendCfg._id = 'axis-' + dimType;
if (!Util.isNil(index)) {
appendCfg._id = 'axis-' + dimType + index;
}
new Axis[type](Util.mix(axis, appendCfg));
});
}
clear() {
this.axes = {};
this.frontPlot.clear();
this.backPlot.clear();
}
}
module.exports = AxisController;