| /* | |
| * 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 LineDraw from '../helper/LineDraw'; | |
| import EffectLine from '../helper/EffectLine'; | |
| import Line from '../helper/Line'; | |
| import Polyline from '../helper/Polyline'; | |
| import EffectPolyline from '../helper/EffectPolyline'; | |
| import LargeLineDraw from '../helper/LargeLineDraw'; | |
| import linesLayout from './linesLayout'; | |
| import {createClipPath} from '../helper/createClipPathFromCoordSys'; | |
| import ChartView from '../../view/Chart'; | |
| import LinesSeriesModel from './LinesSeries'; | |
| import GlobalModel from '../../model/Global'; | |
| import ExtensionAPI from '../../core/ExtensionAPI'; | |
| import CanvasPainter from 'zrender/src/canvas/Painter'; | |
| import { StageHandlerProgressParams, StageHandlerProgressExecutor } from '../../util/types'; | |
| import SeriesData from '../../data/SeriesData'; | |
| import type Polar from '../../coord/polar/Polar'; | |
| import type Cartesian2D from '../../coord/cartesian/Cartesian2D'; | |
| import Element from 'zrender/src/Element'; | |
| class LinesView extends ChartView { | |
| static readonly type = 'lines'; | |
| readonly type = LinesView.type; | |
| private _lastZlevel: number; | |
| private _finished: boolean; | |
| private _lineDraw: LineDraw | LargeLineDraw; | |
| private _hasEffet: boolean; | |
| private _isPolyline: boolean; | |
| private _isLargeDraw: boolean; | |
| render(seriesModel: LinesSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) { | |
| const data = seriesModel.getData(); | |
| const lineDraw = this._updateLineDraw(data, seriesModel); | |
| const zlevel = seriesModel.get('zlevel'); | |
| const trailLength = seriesModel.get(['effect', 'trailLength']); | |
| const zr = api.getZr(); | |
| // Avoid the drag cause ghost shadow | |
| // FIXME Better way ? | |
| // SVG doesn't support | |
| const isSvg = zr.painter.getType() === 'svg'; | |
| if (!isSvg) { | |
| (zr.painter as CanvasPainter).getLayer(zlevel).clear(true); | |
| } | |
| // Config layer with motion blur | |
| if (this._lastZlevel != null && !isSvg) { | |
| zr.configLayer(this._lastZlevel, { | |
| motionBlur: false | |
| }); | |
| } | |
| if (this._showEffect(seriesModel) && trailLength > 0) { | |
| if (!isSvg) { | |
| zr.configLayer(zlevel, { | |
| motionBlur: true, | |
| lastFrameAlpha: Math.max(Math.min(trailLength / 10 + 0.9, 1), 0) | |
| }); | |
| } | |
| else if (__DEV__) { | |
| console.warn('SVG render mode doesn\'t support lines with trail effect'); | |
| } | |
| } | |
| lineDraw.updateData(data as SeriesData); | |
| const clipPath = seriesModel.get('clip', true) && createClipPath( | |
| (seriesModel.coordinateSystem as Polar | Cartesian2D), false, seriesModel | |
| ); | |
| if (clipPath) { | |
| this.group.setClipPath(clipPath); | |
| } | |
| else { | |
| this.group.removeClipPath(); | |
| } | |
| this._lastZlevel = zlevel; | |
| this._finished = true; | |
| } | |
| incrementalPrepareRender(seriesModel: LinesSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) { | |
| const data = seriesModel.getData(); | |
| const lineDraw = this._updateLineDraw(data, seriesModel); | |
| lineDraw.incrementalPrepareUpdate(data as any); | |
| this._clearLayer(api); | |
| this._finished = false; | |
| } | |
| incrementalRender( | |
| taskParams: StageHandlerProgressParams, | |
| seriesModel: LinesSeriesModel, | |
| ecModel: GlobalModel | |
| ) { | |
| this._lineDraw.incrementalUpdate(taskParams, seriesModel.getData() as any); | |
| this._finished = taskParams.end === seriesModel.getData().count(); | |
| } | |
| eachRendered(cb: (el: Element) => boolean | void) { | |
| this._lineDraw && this._lineDraw.eachRendered(cb); | |
| } | |
| updateTransform(seriesModel: LinesSeriesModel, ecModel: GlobalModel, api: ExtensionAPI) { | |
| const data = seriesModel.getData(); | |
| const pipelineContext = seriesModel.pipelineContext; | |
| if (!this._finished || pipelineContext.large || pipelineContext.progressiveRender) { | |
| // TODO Don't have to do update in large mode. Only do it when there are millions of data. | |
| return { | |
| update: true | |
| } as const; | |
| } | |
| else { | |
| // TODO Use same logic with ScatterView. | |
| // Manually update layout | |
| const res = linesLayout.reset(seriesModel, ecModel, api) as StageHandlerProgressExecutor; | |
| if (res.progress) { | |
| res.progress({ | |
| start: 0, | |
| end: data.count(), | |
| count: data.count() | |
| }, data); | |
| } | |
| // Not in large mode | |
| (this._lineDraw as LineDraw).updateLayout(); | |
| this._clearLayer(api); | |
| } | |
| } | |
| _updateLineDraw(data: SeriesData, seriesModel: LinesSeriesModel) { | |
| let lineDraw = this._lineDraw; | |
| const hasEffect = this._showEffect(seriesModel); | |
| const isPolyline = !!seriesModel.get('polyline'); | |
| const pipelineContext = seriesModel.pipelineContext; | |
| const isLargeDraw = pipelineContext.large; | |
| if (__DEV__) { | |
| if (hasEffect && isLargeDraw) { | |
| console.warn('Large lines not support effect'); | |
| } | |
| } | |
| if (!lineDraw | |
| || hasEffect !== this._hasEffet | |
| || isPolyline !== this._isPolyline | |
| || isLargeDraw !== this._isLargeDraw | |
| ) { | |
| if (lineDraw) { | |
| lineDraw.remove(); | |
| } | |
| lineDraw = this._lineDraw = isLargeDraw | |
| ? new LargeLineDraw() | |
| : new LineDraw( | |
| isPolyline | |
| ? (hasEffect ? EffectPolyline : Polyline) | |
| : (hasEffect ? EffectLine : Line) | |
| ); | |
| this._hasEffet = hasEffect; | |
| this._isPolyline = isPolyline; | |
| this._isLargeDraw = isLargeDraw; | |
| } | |
| this.group.add(lineDraw.group); | |
| return lineDraw; | |
| } | |
| private _showEffect(seriesModel: LinesSeriesModel) { | |
| return !!seriesModel.get(['effect', 'show']); | |
| } | |
| _clearLayer(api: ExtensionAPI) { | |
| // Not use motion when dragging or zooming | |
| const zr = api.getZr(); | |
| const isSvg = zr.painter.getType() === 'svg'; | |
| if (!isSvg && this._lastZlevel != null) { | |
| (zr.painter as CanvasPainter).getLayer(this._lastZlevel).clear(true); | |
| } | |
| } | |
| remove(ecModel: GlobalModel, api: ExtensionAPI) { | |
| this._lineDraw && this._lineDraw.remove(); | |
| this._lineDraw = null; | |
| // Clear motion when lineDraw is removed | |
| this._clearLayer(api); | |
| } | |
| dispose(ecModel: GlobalModel, api: ExtensionAPI) { | |
| this.remove(ecModel, api); | |
| } | |
| } | |
| export default LinesView; |