| /* | |
| * 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 LineGroup from './Line'; | |
| import SeriesData from '../../data/SeriesData'; | |
| import { | |
| StageHandlerProgressParams, | |
| LineStyleOption, | |
| LineLabelOption, | |
| ColorString, | |
| AnimationOptionMixin, | |
| ZRStyleProps, | |
| StatesOptionMixin, | |
| DisplayState, | |
| LabelOption, | |
| DefaultEmphasisFocus, | |
| BlurScope | |
| } from '../../util/types'; | |
| import Displayable from 'zrender/src/graphic/Displayable'; | |
| import Model from '../../model/Model'; | |
| import { getLabelStatesModels } from '../../label/labelStyle'; | |
| import Element from 'zrender/src/Element'; | |
| interface LineLike extends graphic.Group { | |
| updateData(data: SeriesData, idx: number, scope?: LineDrawSeriesScope): void | |
| updateLayout(data: SeriesData, idx: number): void | |
| fadeOut?(cb: () => void): void | |
| } | |
| interface LineLikeCtor { | |
| new(data: SeriesData, idx: number, scope?: LineDrawSeriesScope): LineLike | |
| } | |
| interface LineDrawStateOption { | |
| lineStyle?: LineStyleOption | |
| label?: LineLabelOption | |
| } | |
| export interface LineDrawModelOption extends LineDrawStateOption, | |
| StatesOptionMixin<LineDrawStateOption, { | |
| emphasis?: { | |
| focus?: DefaultEmphasisFocus | |
| } | |
| }> { | |
| // If has effect | |
| effect?: { | |
| show?: boolean | |
| period?: number | |
| delay?: number | ((idx: number) => number) | |
| /** | |
| * If move with constant speed px/sec | |
| * period will be ignored if this property is > 0, | |
| */ | |
| constantSpeed?: number | |
| symbol?: string | |
| symbolSize?: number | number[] | |
| loop?: boolean | |
| roundTrip?: boolean | |
| /** | |
| * Length of trail, 0 - 1 | |
| */ | |
| trailLength?: number | |
| /** | |
| * Default to be same with lineStyle.color | |
| */ | |
| color?: ColorString | |
| } | |
| } | |
| type ListForLineDraw = SeriesData<Model<LineDrawModelOption & AnimationOptionMixin>>; | |
| export interface LineDrawSeriesScope { | |
| lineStyle?: ZRStyleProps | |
| emphasisLineStyle?: ZRStyleProps | |
| blurLineStyle?: ZRStyleProps | |
| selectLineStyle?: ZRStyleProps | |
| labelStatesModels: Record<DisplayState, Model<LabelOption>> | |
| focus?: DefaultEmphasisFocus | |
| blurScope?: BlurScope | |
| emphasisDisabled?: boolean; | |
| } | |
| class LineDraw { | |
| group = new graphic.Group(); | |
| private _LineCtor: LineLikeCtor; | |
| private _lineData: ListForLineDraw; | |
| private _seriesScope: LineDrawSeriesScope; | |
| private _progressiveEls: LineLike[]; | |
| constructor(LineCtor?: LineLikeCtor) { | |
| this._LineCtor = LineCtor || LineGroup; | |
| } | |
| updateData(lineData: ListForLineDraw) { | |
| // Remove progressive els. | |
| this._progressiveEls = null; | |
| const lineDraw = this; | |
| const group = lineDraw.group; | |
| const oldLineData = lineDraw._lineData; | |
| lineDraw._lineData = lineData; | |
| // There is no oldLineData only when first rendering or switching from | |
| // stream mode to normal mode, where previous elements should be removed. | |
| if (!oldLineData) { | |
| group.removeAll(); | |
| } | |
| const seriesScope = makeSeriesScope(lineData); | |
| lineData.diff(oldLineData) | |
| .add((idx) => { | |
| this._doAdd(lineData, idx, seriesScope); | |
| }) | |
| .update((newIdx, oldIdx) => { | |
| this._doUpdate(oldLineData, lineData, oldIdx, newIdx, seriesScope); | |
| }) | |
| .remove((idx) => { | |
| group.remove(oldLineData.getItemGraphicEl(idx)); | |
| }) | |
| .execute(); | |
| }; | |
| updateLayout() { | |
| const lineData = this._lineData; | |
| // Do not support update layout in incremental mode. | |
| if (!lineData) { | |
| return; | |
| } | |
| lineData.eachItemGraphicEl(function (el: LineLike, idx) { | |
| el.updateLayout(lineData, idx); | |
| }, this); | |
| }; | |
| incrementalPrepareUpdate(lineData: ListForLineDraw) { | |
| this._seriesScope = makeSeriesScope(lineData); | |
| this._lineData = null; | |
| this.group.removeAll(); | |
| }; | |
| incrementalUpdate(taskParams: StageHandlerProgressParams, lineData: ListForLineDraw) { | |
| this._progressiveEls = []; | |
| function updateIncrementalAndHover(el: Displayable) { | |
| if (!el.isGroup && !isEffectObject(el)) { | |
| el.incremental = true; | |
| el.ensureState('emphasis').hoverLayer = true; | |
| } | |
| } | |
| for (let idx = taskParams.start; idx < taskParams.end; idx++) { | |
| const itemLayout = lineData.getItemLayout(idx); | |
| if (lineNeedsDraw(itemLayout)) { | |
| const el = new this._LineCtor(lineData, idx, this._seriesScope); | |
| el.traverse(updateIncrementalAndHover); | |
| this.group.add(el); | |
| lineData.setItemGraphicEl(idx, el); | |
| this._progressiveEls.push(el); | |
| } | |
| } | |
| }; | |
| remove() { | |
| this.group.removeAll(); | |
| }; | |
| eachRendered(cb: (el: Element) => boolean | void) { | |
| graphic.traverseElements(this._progressiveEls || this.group, cb); | |
| } | |
| private _doAdd( | |
| lineData: ListForLineDraw, | |
| idx: number, | |
| seriesScope: LineDrawSeriesScope | |
| ) { | |
| const itemLayout = lineData.getItemLayout(idx); | |
| if (!lineNeedsDraw(itemLayout)) { | |
| return; | |
| } | |
| const el = new this._LineCtor(lineData, idx, seriesScope); | |
| lineData.setItemGraphicEl(idx, el); | |
| this.group.add(el); | |
| } | |
| private _doUpdate( | |
| oldLineData: ListForLineDraw, | |
| newLineData: ListForLineDraw, | |
| oldIdx: number, | |
| newIdx: number, | |
| seriesScope: LineDrawSeriesScope | |
| ) { | |
| let itemEl = oldLineData.getItemGraphicEl(oldIdx) as LineLike; | |
| if (!lineNeedsDraw(newLineData.getItemLayout(newIdx))) { | |
| this.group.remove(itemEl); | |
| return; | |
| } | |
| if (!itemEl) { | |
| itemEl = new this._LineCtor(newLineData, newIdx, seriesScope); | |
| } | |
| else { | |
| itemEl.updateData(newLineData, newIdx, seriesScope); | |
| } | |
| newLineData.setItemGraphicEl(newIdx, itemEl); | |
| this.group.add(itemEl); | |
| } | |
| } | |
| function isEffectObject(el: Displayable) { | |
| return el.animators && el.animators.length > 0; | |
| } | |
| function makeSeriesScope(lineData: ListForLineDraw): LineDrawSeriesScope { | |
| const hostModel = lineData.hostModel; | |
| const emphasisModel = hostModel.getModel('emphasis'); | |
| return { | |
| lineStyle: hostModel.getModel('lineStyle').getLineStyle(), | |
| emphasisLineStyle: emphasisModel.getModel(['lineStyle']).getLineStyle(), | |
| blurLineStyle: hostModel.getModel(['blur', 'lineStyle']).getLineStyle(), | |
| selectLineStyle: hostModel.getModel(['select', 'lineStyle']).getLineStyle(), | |
| emphasisDisabled: emphasisModel.get('disabled'), | |
| blurScope: emphasisModel.get('blurScope'), | |
| focus: emphasisModel.get('focus'), | |
| labelStatesModels: getLabelStatesModels(hostModel) | |
| }; | |
| } | |
| function isPointNaN(pt: number[]) { | |
| return isNaN(pt[0]) || isNaN(pt[1]); | |
| } | |
| function lineNeedsDraw(pts: number[][]) { | |
| return pts && !isPointNaN(pts[0]) && !isPointNaN(pts[1]); | |
| } | |
| export default LineDraw; |