| /* | |
| * 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 { setStatesStylesFromModel, toggleHoverEmphasis } from '../../util/states'; | |
| import {createTextStyle, setLabelValueAnimation, animateLabelValue} from '../../label/labelStyle'; | |
| import ChartView from '../../view/Chart'; | |
| import {parsePercent, round, linearMap} from '../../util/number'; | |
| import GaugeSeriesModel, { GaugeDataItemOption } from './GaugeSeries'; | |
| import GlobalModel from '../../model/Global'; | |
| import ExtensionAPI from '../../core/ExtensionAPI'; | |
| import { ColorString, ECElement } from '../../util/types'; | |
| import SeriesData from '../../data/SeriesData'; | |
| import Sausage from '../../util/shape/sausage'; | |
| import {createSymbol} from '../../util/symbol'; | |
| import ZRImage from 'zrender/src/graphic/Image'; | |
| import { extend, isFunction, isString, isNumber, each } from 'zrender/src/core/util'; | |
| import {setCommonECData} from '../../util/innerStore'; | |
| import { normalizeArcAngles } from 'zrender/src/core/PathProxy'; | |
| type ECSymbol = ReturnType<typeof createSymbol>; | |
| interface PosInfo { | |
| cx: number | |
| cy: number | |
| r: number | |
| } | |
| function parsePosition(seriesModel: GaugeSeriesModel, api: ExtensionAPI): PosInfo { | |
| const center = seriesModel.get('center'); | |
| const width = api.getWidth(); | |
| const height = api.getHeight(); | |
| const size = Math.min(width, height); | |
| const cx = parsePercent(center[0], api.getWidth()); | |
| const cy = parsePercent(center[1], api.getHeight()); | |
| const r = parsePercent(seriesModel.get('radius'), size / 2); | |
| return { | |
| cx: cx, | |
| cy: cy, | |
| r: r | |
| }; | |
| } | |
| function formatLabel(value: number, labelFormatter: string | ((value: number) => string)): string { | |
| let label = value == null ? '' : (value + ''); | |
| if (labelFormatter) { | |
| if (isString(labelFormatter)) { | |
| label = labelFormatter.replace('{value}', label); | |
| } | |
| else if (isFunction(labelFormatter)) { | |
| label = labelFormatter(value); | |
| } | |
| } | |
| return label; | |
| } | |
| class GaugeView extends ChartView { | |
| static type = 'gauge' as const; | |
| type = GaugeView.type; | |
| private _data: SeriesData; | |
| private _progressEls: graphic.Path[]; | |
| private _titleEls: graphic.Text[]; | |
| private _detailEls: graphic.Text[]; | |
| render(seriesModel: GaugeSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) { | |
| this.group.removeAll(); | |
| const colorList = seriesModel.get(['axisLine', 'lineStyle', 'color']); | |
| const posInfo = parsePosition(seriesModel, api); | |
| this._renderMain( | |
| seriesModel, ecModel, api, colorList, posInfo | |
| ); | |
| this._data = seriesModel.getData(); | |
| } | |
| dispose() {} | |
| _renderMain( | |
| seriesModel: GaugeSeriesModel, | |
| ecModel: GlobalModel, | |
| api: ExtensionAPI, | |
| colorList: [number, ColorString][], | |
| posInfo: PosInfo | |
| ) { | |
| const group = this.group; | |
| const clockwise = seriesModel.get('clockwise'); | |
| let startAngle = -seriesModel.get('startAngle') / 180 * Math.PI; | |
| let endAngle = -seriesModel.get('endAngle') / 180 * Math.PI; | |
| const axisLineModel = seriesModel.getModel('axisLine'); | |
| const roundCap = axisLineModel.get('roundCap'); | |
| const MainPath = roundCap ? Sausage : graphic.Sector; | |
| const showAxis = axisLineModel.get('show'); | |
| const lineStyleModel = axisLineModel.getModel('lineStyle'); | |
| const axisLineWidth = lineStyleModel.get('width'); | |
| const angles = [startAngle, endAngle]; | |
| normalizeArcAngles(angles, !clockwise); | |
| startAngle = angles[0]; | |
| endAngle = angles[1]; | |
| const angleRangeSpan = endAngle - startAngle; | |
| let prevEndAngle = startAngle; | |
| const sectors: (Sausage | graphic.Sector)[] = []; | |
| for (let i = 0; showAxis && i < colorList.length; i++) { | |
| // Clamp | |
| const percent = Math.min(Math.max(colorList[i][0], 0), 1); | |
| endAngle = startAngle + angleRangeSpan * percent; | |
| const sector = new MainPath({ | |
| 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', 'width'] | |
| )); | |
| sectors.push(sector); | |
| prevEndAngle = endAngle; | |
| } | |
| sectors.reverse(); | |
| each(sectors, sector => group.add(sector)); | |
| const getColor = function (percent: number) { | |
| // Less than 0 | |
| if (percent <= 0) { | |
| return colorList[0][1]; | |
| } | |
| let i; | |
| for (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]; | |
| }; | |
| this._renderTicks( | |
| seriesModel, ecModel, api, getColor, posInfo, | |
| startAngle, endAngle, clockwise, axisLineWidth | |
| ); | |
| this._renderTitleAndDetail( | |
| seriesModel, ecModel, api, getColor, posInfo | |
| ); | |
| this._renderAnchor(seriesModel, posInfo); | |
| this._renderPointer( | |
| seriesModel, ecModel, api, getColor, posInfo, | |
| startAngle, endAngle, clockwise, axisLineWidth | |
| ); | |
| } | |
| _renderTicks( | |
| seriesModel: GaugeSeriesModel, | |
| ecModel: GlobalModel, | |
| api: ExtensionAPI, | |
| getColor: (percent: number) => ColorString, | |
| posInfo: PosInfo, | |
| startAngle: number, | |
| endAngle: number, | |
| clockwise: boolean, | |
| axisLineWidth: number | |
| ) { | |
| const group = this.group; | |
| const cx = posInfo.cx; | |
| const cy = posInfo.cy; | |
| const r = posInfo.r; | |
| const minVal = +seriesModel.get('min'); | |
| const maxVal = +seriesModel.get('max'); | |
| const splitLineModel = seriesModel.getModel('splitLine'); | |
| const tickModel = seriesModel.getModel('axisTick'); | |
| const labelModel = seriesModel.getModel('axisLabel'); | |
| const splitNumber = seriesModel.get('splitNumber'); | |
| const subSplitNumber = tickModel.get('splitNumber'); | |
| const splitLineLen = parsePercent( | |
| splitLineModel.get('length'), r | |
| ); | |
| const tickLen = parsePercent( | |
| tickModel.get('length'), r | |
| ); | |
| let angle = startAngle; | |
| const step = (endAngle - startAngle) / splitNumber; | |
| const subStep = step / subSplitNumber; | |
| const splitLineStyle = splitLineModel.getModel('lineStyle').getLineStyle(); | |
| const tickLineStyle = tickModel.getModel('lineStyle').getLineStyle(); | |
| const splitLineDistance = splitLineModel.get('distance'); | |
| let unitX; | |
| let unitY; | |
| for (let i = 0; i <= splitNumber; i++) { | |
| unitX = Math.cos(angle); | |
| unitY = Math.sin(angle); | |
| // Split line | |
| if (splitLineModel.get('show')) { | |
| const distance = splitLineDistance ? splitLineDistance + axisLineWidth : axisLineWidth; | |
| const splitLine = new graphic.Line({ | |
| shape: { | |
| x1: unitX * (r - distance) + cx, | |
| y1: unitY * (r - distance) + cy, | |
| x2: unitX * (r - splitLineLen - distance) + cx, | |
| y2: unitY * (r - splitLineLen - distance) + cy | |
| }, | |
| style: splitLineStyle, | |
| silent: true | |
| }); | |
| if (splitLineStyle.stroke === 'auto') { | |
| splitLine.setStyle({ | |
| stroke: getColor(i / splitNumber) | |
| }); | |
| } | |
| group.add(splitLine); | |
| } | |
| // Label | |
| if (labelModel.get('show')) { | |
| const distance = labelModel.get('distance') + splitLineDistance; | |
| const label = formatLabel( | |
| round(i / splitNumber * (maxVal - minVal) + minVal), | |
| labelModel.get('formatter') | |
| ); | |
| const autoColor = getColor(i / splitNumber); | |
| const textStyleX = unitX * (r - splitLineLen - distance) + cx; | |
| const textStyleY = unitY * (r - splitLineLen - distance) + cy; | |
| const rotateType = labelModel.get('rotate'); | |
| let rotate = 0; | |
| if (rotateType === 'radial') { | |
| rotate = -angle + 2 * Math.PI; | |
| if (rotate > Math.PI / 2) { | |
| rotate += Math.PI; | |
| } | |
| } | |
| else if (rotateType === 'tangential') { | |
| rotate = -angle - Math.PI / 2; | |
| } | |
| else if (isNumber(rotateType)) { | |
| rotate = rotateType * Math.PI / 180; | |
| } | |
| if (rotate === 0) { | |
| group.add(new graphic.Text({ | |
| style: createTextStyle(labelModel, { | |
| text: label, | |
| x: textStyleX, | |
| y: textStyleY, | |
| verticalAlign: unitY < -0.8 ? 'top' : (unitY > 0.8 ? 'bottom' : 'middle'), | |
| align: unitX < -0.4 ? 'left' : (unitX > 0.4 ? 'right' : 'center') | |
| }, { | |
| inheritColor: autoColor | |
| }), | |
| silent: true | |
| })); | |
| } | |
| else { | |
| group.add(new graphic.Text({ | |
| style: createTextStyle(labelModel, { | |
| text: label, | |
| x: textStyleX, | |
| y: textStyleY, | |
| verticalAlign: 'middle', | |
| align: 'center' | |
| }, { | |
| inheritColor: autoColor | |
| }), | |
| silent: true, | |
| originX: textStyleX, | |
| originY: textStyleY, | |
| rotation: rotate | |
| })); | |
| } | |
| } | |
| // Axis tick | |
| if (tickModel.get('show') && i !== splitNumber) { | |
| let distance = tickModel.get('distance'); | |
| distance = distance ? distance + axisLineWidth : axisLineWidth; | |
| for (let j = 0; j <= subSplitNumber; j++) { | |
| unitX = Math.cos(angle); | |
| unitY = Math.sin(angle); | |
| const tickLine = new graphic.Line({ | |
| shape: { | |
| x1: unitX * (r - distance) + cx, | |
| y1: unitY * (r - distance) + cy, | |
| x2: unitX * (r - tickLen - distance) + cx, | |
| y2: unitY * (r - tickLen - distance) + 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( | |
| seriesModel: GaugeSeriesModel, | |
| ecModel: GlobalModel, | |
| api: ExtensionAPI, | |
| getColor: (percent: number) => ColorString, | |
| posInfo: PosInfo, | |
| startAngle: number, | |
| endAngle: number, | |
| clockwise: boolean, | |
| axisLineWidth: number | |
| ) { | |
| const group = this.group; | |
| const oldData = this._data; | |
| const oldProgressData = this._progressEls; | |
| const progressList = [] as graphic.Path[]; | |
| const showPointer = seriesModel.get(['pointer', 'show']); | |
| const progressModel = seriesModel.getModel('progress'); | |
| const showProgress = progressModel.get('show'); | |
| const data = seriesModel.getData(); | |
| const valueDim = data.mapDimension('value'); | |
| const minVal = +seriesModel.get('min'); | |
| const maxVal = +seriesModel.get('max'); | |
| const valueExtent = [minVal, maxVal]; | |
| const angleExtent = [startAngle, endAngle]; | |
| function createPointer(idx: number, angle: number) { | |
| const itemModel = data.getItemModel<GaugeDataItemOption>(idx); | |
| const pointerModel = itemModel.getModel('pointer'); | |
| const pointerWidth = parsePercent(pointerModel.get('width'), posInfo.r); | |
| const pointerLength = parsePercent(pointerModel.get('length'), posInfo.r); | |
| const pointerStr = seriesModel.get(['pointer', 'icon']); | |
| const pointerOffset = pointerModel.get('offsetCenter'); | |
| const pointerOffsetX = parsePercent(pointerOffset[0], posInfo.r); | |
| const pointerOffsetY = parsePercent(pointerOffset[1], posInfo.r); | |
| const pointerKeepAspect = pointerModel.get('keepAspect'); | |
| let pointer; | |
| // not exist icon type will be set 'rect' | |
| if (pointerStr) { | |
| pointer = createSymbol( | |
| pointerStr, | |
| pointerOffsetX - pointerWidth / 2, | |
| pointerOffsetY - pointerLength, | |
| pointerWidth, | |
| pointerLength, | |
| null, | |
| pointerKeepAspect | |
| ) as graphic.Path; | |
| } | |
| else { | |
| pointer = new PointerPath({ | |
| shape: { | |
| angle: -Math.PI / 2, | |
| width: pointerWidth, | |
| r: pointerLength, | |
| x: pointerOffsetX, | |
| y: pointerOffsetY | |
| } | |
| }); | |
| } | |
| pointer.rotation = -(angle + Math.PI / 2); | |
| pointer.x = posInfo.cx; | |
| pointer.y = posInfo.cy; | |
| return pointer; | |
| } | |
| function createProgress(idx: number, endAngle: number) { | |
| const roundCap = progressModel.get('roundCap'); | |
| const ProgressPath = roundCap ? Sausage : graphic.Sector; | |
| const isOverlap = progressModel.get('overlap'); | |
| const progressWidth = isOverlap ? progressModel.get('width') : axisLineWidth / data.count(); | |
| const r0 = isOverlap ? posInfo.r - progressWidth : posInfo.r - (idx + 1) * progressWidth; | |
| const r = isOverlap ? posInfo.r : posInfo.r - idx * progressWidth; | |
| const progress = new ProgressPath({ | |
| shape: { | |
| startAngle: startAngle, | |
| endAngle: endAngle, | |
| cx: posInfo.cx, | |
| cy: posInfo.cy, | |
| clockwise: clockwise, | |
| r0: r0, | |
| r: r | |
| } | |
| }); | |
| isOverlap && (progress.z2 = maxVal - (data.get(valueDim, idx) as number) % maxVal); | |
| return progress; | |
| } | |
| if (showProgress || showPointer) { | |
| data.diff(oldData) | |
| .add(function (idx) { | |
| const val = data.get(valueDim, idx) as number; | |
| if (showPointer) { | |
| const pointer = createPointer(idx, startAngle); | |
| // TODO hide pointer on NaN value? | |
| graphic.initProps(pointer, { | |
| rotation: -( | |
| (isNaN(+val) ? angleExtent[0] : linearMap(val, valueExtent, angleExtent, true)) | |
| + Math.PI / 2 | |
| ) | |
| }, seriesModel); | |
| group.add(pointer); | |
| data.setItemGraphicEl(idx, pointer); | |
| } | |
| if (showProgress) { | |
| const progress = createProgress(idx, startAngle) as graphic.Sector; | |
| const isClip = progressModel.get('clip'); | |
| graphic.initProps(progress, { | |
| shape: { | |
| endAngle: linearMap(val, valueExtent, angleExtent, isClip) | |
| } | |
| }, seriesModel); | |
| group.add(progress); | |
| // Add data index and series index for indexing the data by element | |
| // Useful in tooltip | |
| setCommonECData(seriesModel.seriesIndex, data.dataType, idx, progress); | |
| progressList[idx] = progress; | |
| } | |
| }) | |
| .update(function (newIdx, oldIdx) { | |
| const val = data.get(valueDim, newIdx) as number; | |
| if (showPointer) { | |
| const previousPointer = oldData.getItemGraphicEl(oldIdx) as PointerPath; | |
| const previousRotate = previousPointer ? previousPointer.rotation : startAngle; | |
| const pointer = createPointer(newIdx, previousRotate); | |
| pointer.rotation = previousRotate; | |
| graphic.updateProps(pointer, { | |
| rotation: -( | |
| (isNaN(+val) ? angleExtent[0] : linearMap(val, valueExtent, angleExtent, true)) | |
| + Math.PI / 2 | |
| ) | |
| }, seriesModel); | |
| group.add(pointer); | |
| data.setItemGraphicEl(newIdx, pointer); | |
| } | |
| if (showProgress) { | |
| const previousProgress = oldProgressData[oldIdx]; | |
| const previousEndAngle = previousProgress ? previousProgress.shape.endAngle : startAngle; | |
| const progress = createProgress(newIdx, previousEndAngle) as graphic.Sector; | |
| const isClip = progressModel.get('clip'); | |
| graphic.updateProps(progress, { | |
| shape: { | |
| endAngle: linearMap(val, valueExtent, angleExtent, isClip) | |
| } | |
| }, seriesModel); | |
| group.add(progress); | |
| // Add data index and series index for indexing the data by element | |
| // Useful in tooltip | |
| setCommonECData(seriesModel.seriesIndex, data.dataType, newIdx, progress); | |
| progressList[newIdx] = progress; | |
| } | |
| }) | |
| .execute(); | |
| data.each(function (idx) { | |
| const itemModel = data.getItemModel<GaugeDataItemOption>(idx); | |
| const emphasisModel = itemModel.getModel('emphasis'); | |
| const focus = emphasisModel.get('focus'); | |
| const blurScope = emphasisModel.get('blurScope'); | |
| const emphasisDisabled = emphasisModel.get('disabled'); | |
| if (showPointer) { | |
| const pointer = data.getItemGraphicEl(idx) as ECSymbol; | |
| const symbolStyle = data.getItemVisual(idx, 'style'); | |
| const visualColor = symbolStyle.fill; | |
| if (pointer instanceof ZRImage) { | |
| const pathStyle = pointer.style; | |
| pointer.useStyle(extend({ | |
| image: pathStyle.image, | |
| x: pathStyle.x, y: pathStyle.y, | |
| width: pathStyle.width, height: pathStyle.height | |
| }, symbolStyle)); | |
| } | |
| else { | |
| pointer.useStyle(symbolStyle); | |
| pointer.type !== 'pointer' && pointer.setColor(visualColor); | |
| } | |
| pointer.setStyle(itemModel.getModel(['pointer', 'itemStyle']).getItemStyle()); | |
| if (pointer.style.fill === 'auto') { | |
| pointer.setStyle('fill', getColor( | |
| linearMap(data.get(valueDim, idx) as number, valueExtent, [0, 1], true) | |
| )); | |
| } | |
| (pointer as ECElement).z2EmphasisLift = 0; | |
| setStatesStylesFromModel(pointer, itemModel); | |
| toggleHoverEmphasis(pointer, focus, blurScope, emphasisDisabled); | |
| } | |
| if (showProgress) { | |
| const progress = progressList[idx]; | |
| progress.useStyle(data.getItemVisual(idx, 'style')); | |
| progress.setStyle(itemModel.getModel(['progress', 'itemStyle']).getItemStyle()); | |
| (progress as ECElement).z2EmphasisLift = 0; | |
| setStatesStylesFromModel(progress, itemModel); | |
| toggleHoverEmphasis(progress, focus, blurScope, emphasisDisabled); | |
| } | |
| }); | |
| this._progressEls = progressList; | |
| } | |
| } | |
| _renderAnchor( | |
| seriesModel: GaugeSeriesModel, | |
| posInfo: PosInfo | |
| ) { | |
| const anchorModel = seriesModel.getModel('anchor'); | |
| const showAnchor = anchorModel.get('show'); | |
| if (showAnchor) { | |
| const anchorSize = anchorModel.get('size'); | |
| const anchorType = anchorModel.get('icon'); | |
| const offsetCenter = anchorModel.get('offsetCenter'); | |
| const anchorKeepAspect = anchorModel.get('keepAspect'); | |
| const anchor = createSymbol( | |
| anchorType, | |
| posInfo.cx - anchorSize / 2 + parsePercent(offsetCenter[0], posInfo.r), | |
| posInfo.cy - anchorSize / 2 + parsePercent(offsetCenter[1], posInfo.r), | |
| anchorSize, | |
| anchorSize, | |
| null, | |
| anchorKeepAspect | |
| ) as graphic.Path; | |
| anchor.z2 = anchorModel.get('showAbove') ? 1 : 0; | |
| anchor.setStyle(anchorModel.getModel('itemStyle').getItemStyle()); | |
| this.group.add(anchor); | |
| } | |
| } | |
| _renderTitleAndDetail( | |
| seriesModel: GaugeSeriesModel, | |
| ecModel: GlobalModel, | |
| api: ExtensionAPI, | |
| getColor: (percent: number) => ColorString, | |
| posInfo: PosInfo | |
| ) { | |
| const data = seriesModel.getData(); | |
| const valueDim = data.mapDimension('value'); | |
| const minVal = +seriesModel.get('min'); | |
| const maxVal = +seriesModel.get('max'); | |
| const contentGroup = new graphic.Group(); | |
| const newTitleEls: graphic.Text[] = []; | |
| const newDetailEls: graphic.Text[] = []; | |
| const hasAnimation = seriesModel.isAnimationEnabled(); | |
| const showPointerAbove = seriesModel.get(['pointer', 'showAbove']); | |
| data.diff(this._data) | |
| .add((idx) => { | |
| newTitleEls[idx] = new graphic.Text({ | |
| silent: true | |
| }); | |
| newDetailEls[idx] = new graphic.Text({ | |
| silent: true | |
| }); | |
| }) | |
| .update((idx, oldIdx) => { | |
| newTitleEls[idx] = this._titleEls[oldIdx]; | |
| newDetailEls[idx] = this._detailEls[oldIdx]; | |
| }) | |
| .execute(); | |
| data.each(function (idx) { | |
| const itemModel = data.getItemModel<GaugeDataItemOption>(idx); | |
| const value = data.get(valueDim, idx) as number; | |
| const itemGroup = new graphic.Group(); | |
| const autoColor = getColor( | |
| linearMap(value, [minVal, maxVal], [0, 1], true) | |
| ); | |
| const itemTitleModel = itemModel.getModel('title'); | |
| if (itemTitleModel.get('show')) { | |
| const titleOffsetCenter = itemTitleModel.get('offsetCenter'); | |
| const titleX = posInfo.cx + parsePercent(titleOffsetCenter[0], posInfo.r); | |
| const titleY = posInfo.cy + parsePercent(titleOffsetCenter[1], posInfo.r); | |
| const labelEl = newTitleEls[idx]; | |
| labelEl.attr({ | |
| z2: showPointerAbove ? 0 : 2, | |
| style: createTextStyle(itemTitleModel, { | |
| x: titleX, | |
| y: titleY, | |
| text: data.getName(idx), | |
| align: 'center', | |
| verticalAlign: 'middle' | |
| }, {inheritColor: autoColor}) | |
| }); | |
| itemGroup.add(labelEl); | |
| } | |
| const itemDetailModel = itemModel.getModel('detail'); | |
| if (itemDetailModel.get('show')) { | |
| const detailOffsetCenter = itemDetailModel.get('offsetCenter'); | |
| const detailX = posInfo.cx + parsePercent(detailOffsetCenter[0], posInfo.r); | |
| const detailY = posInfo.cy + parsePercent(detailOffsetCenter[1], posInfo.r); | |
| const width = parsePercent(itemDetailModel.get('width'), posInfo.r); | |
| const height = parsePercent(itemDetailModel.get('height'), posInfo.r); | |
| const detailColor = ( | |
| seriesModel.get(['progress', 'show']) ? data.getItemVisual(idx, 'style').fill : autoColor | |
| ) as string; | |
| const labelEl = newDetailEls[idx]; | |
| const formatter = itemDetailModel.get('formatter'); | |
| labelEl.attr({ | |
| z2: showPointerAbove ? 0 : 2, | |
| style: createTextStyle(itemDetailModel, { | |
| x: detailX, | |
| y: detailY, | |
| text: formatLabel(value, formatter), | |
| width: isNaN(width) ? null : width, | |
| height: isNaN(height) ? null : height, | |
| align: 'center', | |
| verticalAlign: 'middle' | |
| }, {inheritColor: detailColor}) | |
| }); | |
| setLabelValueAnimation( | |
| labelEl, | |
| {normal: itemDetailModel}, | |
| value, | |
| (value: number) => formatLabel(value, formatter) | |
| ); | |
| hasAnimation && animateLabelValue(labelEl, idx, data, seriesModel, { | |
| getFormattedLabel( | |
| labelDataIndex, status, dataType, labelDimIndex, fmt, extendParams | |
| ) { | |
| return formatLabel( | |
| extendParams | |
| ? extendParams.interpolatedValue as typeof value | |
| : value, | |
| formatter | |
| ); | |
| } | |
| }); | |
| itemGroup.add(labelEl); | |
| } | |
| contentGroup.add(itemGroup); | |
| }); | |
| this.group.add(contentGroup); | |
| this._titleEls = newTitleEls; | |
| this._detailEls = newDetailEls; | |
| } | |
| } | |
| export default GaugeView; |