| /* |
| * 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. |
| */ |
| import PointerPath from './PointerPath'; |
| import * as graphic from '../../util/graphic'; |
| import ChartView from '../../view/Chart'; |
| import { parsePercent, round, linearMap } from '../../util/number'; |
| |
| 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 |
| }) |
| })); |
| } |
| } |
| }); |
| export default GaugeView; |