377 lines
13 KiB
JavaScript
377 lines
13 KiB
JavaScript
|
|
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
var PointerPath = require("./PointerPath");
|
|
|
|
var graphic = require("../../util/graphic");
|
|
|
|
var ChartView = require("../../view/Chart");
|
|
|
|
var _number = require("../../util/number");
|
|
|
|
var parsePercent = _number.parsePercent;
|
|
var round = _number.round;
|
|
var linearMap = _number.linearMap;
|
|
|
|
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
function parsePosition(seriesModel, api) {
|
|
var center = seriesModel.get('center');
|
|
var width = api.getWidth();
|
|
var height = api.getHeight();
|
|
var size = Math.min(width, height);
|
|
var cx = parsePercent(center[0], api.getWidth());
|
|
var cy = parsePercent(center[1], api.getHeight());
|
|
var r = parsePercent(seriesModel.get('radius'), size / 2);
|
|
return {
|
|
cx: cx,
|
|
cy: cy,
|
|
r: r
|
|
};
|
|
}
|
|
|
|
function formatLabel(label, labelFormatter) {
|
|
if (labelFormatter) {
|
|
if (typeof labelFormatter === 'string') {
|
|
label = labelFormatter.replace('{value}', label != null ? label : '');
|
|
} else if (typeof labelFormatter === 'function') {
|
|
label = labelFormatter(label);
|
|
}
|
|
}
|
|
|
|
return label;
|
|
}
|
|
|
|
var PI2 = Math.PI * 2;
|
|
var GaugeView = ChartView.extend({
|
|
type: 'gauge',
|
|
render: function (seriesModel, ecModel, api) {
|
|
this.group.removeAll();
|
|
var colorList = seriesModel.get('axisLine.lineStyle.color');
|
|
var posInfo = parsePosition(seriesModel, api);
|
|
|
|
this._renderMain(seriesModel, ecModel, api, colorList, posInfo);
|
|
},
|
|
dispose: function () {},
|
|
_renderMain: function (seriesModel, ecModel, api, colorList, posInfo) {
|
|
var group = this.group;
|
|
var axisLineModel = seriesModel.getModel('axisLine');
|
|
var lineStyleModel = axisLineModel.getModel('lineStyle');
|
|
var clockwise = seriesModel.get('clockwise');
|
|
var startAngle = -seriesModel.get('startAngle') / 180 * Math.PI;
|
|
var endAngle = -seriesModel.get('endAngle') / 180 * Math.PI;
|
|
var angleRangeSpan = (endAngle - startAngle) % PI2;
|
|
var prevEndAngle = startAngle;
|
|
var axisLineWidth = lineStyleModel.get('width');
|
|
var showAxis = axisLineModel.get('show');
|
|
|
|
for (var i = 0; showAxis && i < colorList.length; i++) {
|
|
// Clamp
|
|
var percent = Math.min(Math.max(colorList[i][0], 0), 1);
|
|
var endAngle = startAngle + angleRangeSpan * percent;
|
|
var sector = new graphic.Sector({
|
|
shape: {
|
|
startAngle: prevEndAngle,
|
|
endAngle: endAngle,
|
|
cx: posInfo.cx,
|
|
cy: posInfo.cy,
|
|
clockwise: clockwise,
|
|
r0: posInfo.r - axisLineWidth,
|
|
r: posInfo.r
|
|
},
|
|
silent: true
|
|
});
|
|
sector.setStyle({
|
|
fill: colorList[i][1]
|
|
});
|
|
sector.setStyle(lineStyleModel.getLineStyle( // Because we use sector to simulate arc
|
|
// so the properties for stroking are useless
|
|
['color', 'borderWidth', 'borderColor']));
|
|
group.add(sector);
|
|
prevEndAngle = endAngle;
|
|
}
|
|
|
|
var getColor = function (percent) {
|
|
// Less than 0
|
|
if (percent <= 0) {
|
|
return colorList[0][1];
|
|
}
|
|
|
|
for (var i = 0; i < colorList.length; i++) {
|
|
if (colorList[i][0] >= percent && (i === 0 ? 0 : colorList[i - 1][0]) < percent) {
|
|
return colorList[i][1];
|
|
}
|
|
} // More than 1
|
|
|
|
|
|
return colorList[i - 1][1];
|
|
};
|
|
|
|
if (!clockwise) {
|
|
var tmp = startAngle;
|
|
startAngle = endAngle;
|
|
endAngle = tmp;
|
|
}
|
|
|
|
this._renderTicks(seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise);
|
|
|
|
this._renderPointer(seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise);
|
|
|
|
this._renderTitle(seriesModel, ecModel, api, getColor, posInfo);
|
|
|
|
this._renderDetail(seriesModel, ecModel, api, getColor, posInfo);
|
|
},
|
|
_renderTicks: function (seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise) {
|
|
var group = this.group;
|
|
var cx = posInfo.cx;
|
|
var cy = posInfo.cy;
|
|
var r = posInfo.r;
|
|
var minVal = +seriesModel.get('min');
|
|
var maxVal = +seriesModel.get('max');
|
|
var splitLineModel = seriesModel.getModel('splitLine');
|
|
var tickModel = seriesModel.getModel('axisTick');
|
|
var labelModel = seriesModel.getModel('axisLabel');
|
|
var splitNumber = seriesModel.get('splitNumber');
|
|
var subSplitNumber = tickModel.get('splitNumber');
|
|
var splitLineLen = parsePercent(splitLineModel.get('length'), r);
|
|
var tickLen = parsePercent(tickModel.get('length'), r);
|
|
var angle = startAngle;
|
|
var step = (endAngle - startAngle) / splitNumber;
|
|
var subStep = step / subSplitNumber;
|
|
var splitLineStyle = splitLineModel.getModel('lineStyle').getLineStyle();
|
|
var tickLineStyle = tickModel.getModel('lineStyle').getLineStyle();
|
|
|
|
for (var i = 0; i <= splitNumber; i++) {
|
|
var unitX = Math.cos(angle);
|
|
var unitY = Math.sin(angle); // Split line
|
|
|
|
if (splitLineModel.get('show')) {
|
|
var splitLine = new graphic.Line({
|
|
shape: {
|
|
x1: unitX * r + cx,
|
|
y1: unitY * r + cy,
|
|
x2: unitX * (r - splitLineLen) + cx,
|
|
y2: unitY * (r - splitLineLen) + cy
|
|
},
|
|
style: splitLineStyle,
|
|
silent: true
|
|
});
|
|
|
|
if (splitLineStyle.stroke === 'auto') {
|
|
splitLine.setStyle({
|
|
stroke: getColor(i / splitNumber)
|
|
});
|
|
}
|
|
|
|
group.add(splitLine);
|
|
} // Label
|
|
|
|
|
|
if (labelModel.get('show')) {
|
|
var label = formatLabel(round(i / splitNumber * (maxVal - minVal) + minVal), labelModel.get('formatter'));
|
|
var distance = labelModel.get('distance');
|
|
var autoColor = getColor(i / splitNumber);
|
|
group.add(new graphic.Text({
|
|
style: graphic.setTextStyle({}, labelModel, {
|
|
text: label,
|
|
x: unitX * (r - splitLineLen - distance) + cx,
|
|
y: unitY * (r - splitLineLen - distance) + cy,
|
|
textVerticalAlign: unitY < -0.4 ? 'top' : unitY > 0.4 ? 'bottom' : 'middle',
|
|
textAlign: unitX < -0.4 ? 'left' : unitX > 0.4 ? 'right' : 'center'
|
|
}, {
|
|
autoColor: autoColor
|
|
}),
|
|
silent: true
|
|
}));
|
|
} // Axis tick
|
|
|
|
|
|
if (tickModel.get('show') && i !== splitNumber) {
|
|
for (var j = 0; j <= subSplitNumber; j++) {
|
|
var unitX = Math.cos(angle);
|
|
var unitY = Math.sin(angle);
|
|
var tickLine = new graphic.Line({
|
|
shape: {
|
|
x1: unitX * r + cx,
|
|
y1: unitY * r + cy,
|
|
x2: unitX * (r - tickLen) + cx,
|
|
y2: unitY * (r - tickLen) + cy
|
|
},
|
|
silent: true,
|
|
style: tickLineStyle
|
|
});
|
|
|
|
if (tickLineStyle.stroke === 'auto') {
|
|
tickLine.setStyle({
|
|
stroke: getColor((i + j / subSplitNumber) / splitNumber)
|
|
});
|
|
}
|
|
|
|
group.add(tickLine);
|
|
angle += subStep;
|
|
}
|
|
|
|
angle -= subStep;
|
|
} else {
|
|
angle += step;
|
|
}
|
|
}
|
|
},
|
|
_renderPointer: function (seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise) {
|
|
var group = this.group;
|
|
var oldData = this._data;
|
|
|
|
if (!seriesModel.get('pointer.show')) {
|
|
// Remove old element
|
|
oldData && oldData.eachItemGraphicEl(function (el) {
|
|
group.remove(el);
|
|
});
|
|
return;
|
|
}
|
|
|
|
var valueExtent = [+seriesModel.get('min'), +seriesModel.get('max')];
|
|
var angleExtent = [startAngle, endAngle];
|
|
var data = seriesModel.getData();
|
|
var valueDim = data.mapDimension('value');
|
|
data.diff(oldData).add(function (idx) {
|
|
var pointer = new PointerPath({
|
|
shape: {
|
|
angle: startAngle
|
|
}
|
|
});
|
|
graphic.initProps(pointer, {
|
|
shape: {
|
|
angle: linearMap(data.get(valueDim, idx), valueExtent, angleExtent, true)
|
|
}
|
|
}, seriesModel);
|
|
group.add(pointer);
|
|
data.setItemGraphicEl(idx, pointer);
|
|
}).update(function (newIdx, oldIdx) {
|
|
var pointer = oldData.getItemGraphicEl(oldIdx);
|
|
graphic.updateProps(pointer, {
|
|
shape: {
|
|
angle: linearMap(data.get(valueDim, newIdx), valueExtent, angleExtent, true)
|
|
}
|
|
}, seriesModel);
|
|
group.add(pointer);
|
|
data.setItemGraphicEl(newIdx, pointer);
|
|
}).remove(function (idx) {
|
|
var pointer = oldData.getItemGraphicEl(idx);
|
|
group.remove(pointer);
|
|
}).execute();
|
|
data.eachItemGraphicEl(function (pointer, idx) {
|
|
var itemModel = data.getItemModel(idx);
|
|
var pointerModel = itemModel.getModel('pointer');
|
|
pointer.setShape({
|
|
x: posInfo.cx,
|
|
y: posInfo.cy,
|
|
width: parsePercent(pointerModel.get('width'), posInfo.r),
|
|
r: parsePercent(pointerModel.get('length'), posInfo.r)
|
|
});
|
|
pointer.useStyle(itemModel.getModel('itemStyle').getItemStyle());
|
|
|
|
if (pointer.style.fill === 'auto') {
|
|
pointer.setStyle('fill', getColor(linearMap(data.get(valueDim, idx), valueExtent, [0, 1], true)));
|
|
}
|
|
|
|
graphic.setHoverStyle(pointer, itemModel.getModel('emphasis.itemStyle').getItemStyle());
|
|
});
|
|
this._data = data;
|
|
},
|
|
_renderTitle: function (seriesModel, ecModel, api, getColor, posInfo) {
|
|
var data = seriesModel.getData();
|
|
var valueDim = data.mapDimension('value');
|
|
var titleModel = seriesModel.getModel('title');
|
|
|
|
if (titleModel.get('show')) {
|
|
var offsetCenter = titleModel.get('offsetCenter');
|
|
var x = posInfo.cx + parsePercent(offsetCenter[0], posInfo.r);
|
|
var y = posInfo.cy + parsePercent(offsetCenter[1], posInfo.r);
|
|
var minVal = +seriesModel.get('min');
|
|
var maxVal = +seriesModel.get('max');
|
|
var value = seriesModel.getData().get(valueDim, 0);
|
|
var autoColor = getColor(linearMap(value, [minVal, maxVal], [0, 1], true));
|
|
this.group.add(new graphic.Text({
|
|
silent: true,
|
|
style: graphic.setTextStyle({}, titleModel, {
|
|
x: x,
|
|
y: y,
|
|
// FIXME First data name ?
|
|
text: data.getName(0),
|
|
textAlign: 'center',
|
|
textVerticalAlign: 'middle'
|
|
}, {
|
|
autoColor: autoColor,
|
|
forceRich: true
|
|
})
|
|
}));
|
|
}
|
|
},
|
|
_renderDetail: function (seriesModel, ecModel, api, getColor, posInfo) {
|
|
var detailModel = seriesModel.getModel('detail');
|
|
var minVal = +seriesModel.get('min');
|
|
var maxVal = +seriesModel.get('max');
|
|
|
|
if (detailModel.get('show')) {
|
|
var offsetCenter = detailModel.get('offsetCenter');
|
|
var x = posInfo.cx + parsePercent(offsetCenter[0], posInfo.r);
|
|
var y = posInfo.cy + parsePercent(offsetCenter[1], posInfo.r);
|
|
var width = parsePercent(detailModel.get('width'), posInfo.r);
|
|
var height = parsePercent(detailModel.get('height'), posInfo.r);
|
|
var data = seriesModel.getData();
|
|
var value = data.get(data.mapDimension('value'), 0);
|
|
var autoColor = getColor(linearMap(value, [minVal, maxVal], [0, 1], true));
|
|
this.group.add(new graphic.Text({
|
|
silent: true,
|
|
style: graphic.setTextStyle({}, detailModel, {
|
|
x: x,
|
|
y: y,
|
|
text: formatLabel( // FIXME First data name ?
|
|
value, detailModel.get('formatter')),
|
|
textWidth: isNaN(width) ? null : width,
|
|
textHeight: isNaN(height) ? null : height,
|
|
textAlign: 'center',
|
|
textVerticalAlign: 'middle'
|
|
}, {
|
|
autoColor: autoColor,
|
|
forceRich: true
|
|
})
|
|
}));
|
|
}
|
|
}
|
|
});
|
|
var _default = GaugeView;
|
|
module.exports = _default; |