/* * 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 * as graphic from '../../util/graphic'; import { setStatesStylesFromModel, toggleHoverEmphasis } from '../../util/states'; import ChartView from '../../view/Chart'; import FunnelSeriesModel, {FunnelDataItemOption} from './FunnelSeries'; import GlobalModel from '../../model/Global'; import ExtensionAPI from '../../core/ExtensionAPI'; import SeriesData from '../../data/SeriesData'; import { ColorString } from '../../util/types'; import { setLabelLineStyle, getLabelLineStatesModels } from '../../label/labelGuideHelper'; import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle'; import { saveOldStyle } from '../../animation/basicTransition'; const opacityAccessPath = ['itemStyle', 'opacity'] as const; /** * Piece of pie including Sector, Label, LabelLine */ class FunnelPiece extends graphic.Polygon { constructor(data: SeriesData, idx: number) { super(); const polygon = this; const labelLine = new graphic.Polyline(); const text = new graphic.Text(); polygon.setTextContent(text); this.setTextGuideLine(labelLine); this.updateData(data, idx, true); } updateData(data: SeriesData, idx: number, firstCreate?: boolean) { const polygon = this; const seriesModel = data.hostModel; const itemModel = data.getItemModel(idx); const layout = data.getItemLayout(idx); const emphasisModel = itemModel.getModel('emphasis'); let opacity = itemModel.get(opacityAccessPath); opacity = opacity == null ? 1 : opacity; if (!firstCreate) { saveOldStyle(polygon); } // Update common style polygon.useStyle(data.getItemVisual(idx, 'style')); polygon.style.lineJoin = 'round'; if (firstCreate) { polygon.setShape({ points: layout.points }); polygon.style.opacity = 0; graphic.initProps(polygon, { style: { opacity: opacity } }, seriesModel, idx); } else { graphic.updateProps(polygon, { style: { opacity: opacity }, shape: { points: layout.points } }, seriesModel, idx); } setStatesStylesFromModel(polygon, itemModel); this._updateLabel(data, idx); toggleHoverEmphasis( this, emphasisModel.get('focus'), emphasisModel.get('blurScope'), emphasisModel.get('disabled') ); } _updateLabel(data: SeriesData, idx: number) { const polygon = this; const labelLine = this.getTextGuideLine(); const labelText = polygon.getTextContent(); const seriesModel = data.hostModel; const itemModel = data.getItemModel(idx); const layout = data.getItemLayout(idx); const labelLayout = layout.label; const style = data.getItemVisual(idx, 'style'); const visualColor = style.fill as ColorString; setLabelStyle( // position will not be used in setLabelStyle labelText, getLabelStatesModels(itemModel), { labelFetcher: data.hostModel as FunnelSeriesModel, labelDataIndex: idx, defaultOpacity: style.opacity, defaultText: data.getName(idx) }, { normal: { align: labelLayout.textAlign, verticalAlign: labelLayout.verticalAlign } } ); polygon.setTextConfig({ local: true, inside: !!labelLayout.inside, insideStroke: visualColor, // insideFill: 'auto', outsideFill: visualColor }); const linePoints = labelLayout.linePoints; labelLine.setShape({ points: linePoints }); polygon.textGuideLineConfig = { anchor: linePoints ? new graphic.Point(linePoints[0][0], linePoints[0][1]) : null }; // Make sure update style on labelText after setLabelStyle. // Because setLabelStyle will replace a new style on it. graphic.updateProps(labelText, { style: { x: labelLayout.x, y: labelLayout.y } }, seriesModel, idx); labelText.attr({ rotation: labelLayout.rotation, originX: labelLayout.x, originY: labelLayout.y, z2: 10 }); setLabelLineStyle(polygon, getLabelLineStatesModels(itemModel), { // Default use item visual color stroke: visualColor }); } } class FunnelView extends ChartView { static type = 'funnel' as const; type = FunnelView.type; private _data: SeriesData; ignoreLabelLineUpdate = true; render(seriesModel: FunnelSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) { const data = seriesModel.getData(); const oldData = this._data; const group = this.group; data.diff(oldData) .add(function (idx) { const funnelPiece = new FunnelPiece(data, idx); data.setItemGraphicEl(idx, funnelPiece); group.add(funnelPiece); }) .update(function (newIdx, oldIdx) { const piece = oldData.getItemGraphicEl(oldIdx) as FunnelPiece; piece.updateData(data, newIdx); group.add(piece); data.setItemGraphicEl(newIdx, piece); }) .remove(function (idx) { const piece = oldData.getItemGraphicEl(idx); graphic.removeElementWithFadeOut(piece, seriesModel, idx); }) .execute(); this._data = data; } remove() { this.group.removeAll(); this._data = null; } dispose() {} } export default FunnelView;