| /* |
| * 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 List from '../../data/List'; |
| import { |
| StageHandlerProgressParams, |
| LineStyleOption, |
| LineLabelOption, |
| ColorString, |
| AnimationOptionMixin, |
| ZRStyleProps, |
| StatesOptionMixin, |
| DisplayState, |
| LabelOption |
| } from '../../util/types'; |
| import Displayable from 'zrender/src/graphic/Displayable'; |
| import Model from '../../model/Model'; |
| import { getLabelStatesModels } from '../../label/labelStyle'; |
| |
| interface LineLike extends graphic.Group { |
| updateData(data: List, idx: number, scope?: LineDrawSeriesScope): void |
| updateLayout(data: List, idx: number): void |
| fadeOut?(cb: () => void): void |
| } |
| |
| interface LineLikeCtor { |
| new(data: List, idx: number, scope?: LineDrawSeriesScope): LineLike |
| } |
| |
| interface LineDrawStateOption { |
| lineStyle?: LineStyleOption |
| label?: LineLabelOption |
| } |
| |
| export interface LineDrawModelOption extends LineDrawStateOption, StatesOptionMixin<LineDrawStateOption> { |
| // 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 |
| /** |
| * Length of trail, 0 - 1 |
| */ |
| trailLength?: number |
| /** |
| * Default to be same with lineStyle.color |
| */ |
| color?: ColorString |
| } |
| } |
| |
| type ListForLineDraw = List<Model<LineDrawModelOption & AnimationOptionMixin>>; |
| |
| export interface LineDrawSeriesScope { |
| lineStyle?: ZRStyleProps |
| emphasisLineStyle?: ZRStyleProps |
| blurLineStyle?: ZRStyleProps |
| selectLineStyle?: ZRStyleProps |
| |
| labelStatesModels: Record<DisplayState, Model<LabelOption>> |
| } |
| |
| class LineDraw { |
| group = new graphic.Group(); |
| |
| private _LineCtor: LineLikeCtor; |
| |
| private _lineData: ListForLineDraw; |
| |
| private _seriesScope: LineDrawSeriesScope; |
| |
| constructor(LineCtor?: LineLikeCtor) { |
| this._LineCtor = LineCtor || LineGroup; |
| } |
| |
| isPersistent() { |
| return true; |
| }; |
| |
| updateData(lineData: ListForLineDraw) { |
| 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) { |
| 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); |
| } |
| } |
| }; |
| |
| remove() { |
| this.group.removeAll(); |
| }; |
| |
| 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; |
| return { |
| lineStyle: hostModel.getModel('lineStyle').getLineStyle(), |
| emphasisLineStyle: hostModel.getModel(['emphasis', 'lineStyle']).getLineStyle(), |
| blurLineStyle: hostModel.getModel(['blur', 'lineStyle']).getLineStyle(), |
| selectLineStyle: hostModel.getModel(['select', 'lineStyle']).getLineStyle(), |
| |
| labelStatesModels: getLabelStatesModels(hostModel) |
| }; |
| } |
| |
| function isPointNaN(pt: number[]) { |
| return isNaN(pt[0]) || isNaN(pt[1]); |
| } |
| |
| function lineNeedsDraw(pts: number[][]) { |
| return !isPointNaN(pts[0]) && !isPointNaN(pts[1]); |
| } |
| |
| |
| export default LineDraw; |