From 15375ca696f11447af76ac57c0f0309399ea71ae Mon Sep 17 00:00:00 2001 From: pissang Date: Tue, 16 Nov 2021 12:44:02 +0800 Subject: [PATCH 1/2] fix(dataZoom): optimize dataZoom shadow render performance --- src/component/dataZoom/SliderZoomView.ts | 111 +++++++++++++---------- 1 file changed, 64 insertions(+), 47 deletions(-) diff --git a/src/component/dataZoom/SliderZoomView.ts b/src/component/dataZoom/SliderZoomView.ts index b677224785..ff43663326 100644 --- a/src/component/dataZoom/SliderZoomView.ts +++ b/src/component/dataZoom/SliderZoomView.ts @@ -42,6 +42,7 @@ import { deprecateLog } from '../../util/log'; import { PointLike } from 'zrender/src/core/Point'; import Displayable from 'zrender/src/graphic/Displayable'; import {createTextStyle} from '../../label/labelStyle'; +import SeriesData from '../../data/SeriesData'; const Rect = graphic.Rect; @@ -124,6 +125,12 @@ class SliderZoomView extends DataZoomView { otherAxisInverse: boolean }; + // Cached raw data. Avoid rendering data shadow multiple times. + private _shadowData: SeriesData; + private _shadowDim: string; + private _shadowPolygonPts: number[][]; + private _shadowPolylinePts: number[][]; + init(ecModel: GlobalModel, api: ExtensionAPI) { this.api = api; @@ -350,7 +357,6 @@ class SliderZoomView extends DataZoomView { const size = this._size; const seriesModel = info.series; const data = seriesModel.getRawData(); - const otherDim: string = seriesModel.getShadowDim ? seriesModel.getShadowDim() // @see candlestick : info.otherDim; @@ -359,57 +365,68 @@ class SliderZoomView extends DataZoomView { return; } - let otherDataExtent = data.getDataExtent(otherDim); - // Nice extent. - const otherOffset = (otherDataExtent[1] - otherDataExtent[0]) * 0.3; - otherDataExtent = [ - otherDataExtent[0] - otherOffset, - otherDataExtent[1] + otherOffset - ]; - const otherShadowExtent = [0, size[1]]; - - const thisShadowExtent = [0, size[0]]; + let polygonPts = this._shadowPolygonPts; + let polylinePts = this._shadowPolylinePts; + // Not re-render if data doesn't change. + if (data !== this._shadowData || otherDim !== this._shadowDim) { + let otherDataExtent = data.getDataExtent(otherDim); + // Nice extent. + const otherOffset = (otherDataExtent[1] - otherDataExtent[0]) * 0.3; + otherDataExtent = [ + otherDataExtent[0] - otherOffset, + otherDataExtent[1] + otherOffset + ]; + const otherShadowExtent = [0, size[1]]; + + const thisShadowExtent = [0, size[0]]; + + const areaPoints = [[size[0], 0], [0, 0]]; + const linePoints: number[][] = []; + const step = thisShadowExtent[1] / (data.count() - 1); + let thisCoord = 0; + + // Optimize for large data shadow + const stride = Math.round(data.count() / size[0]); + let lastIsEmpty: boolean; + data.each([otherDim], function (value: ParsedValue, index) { + if (stride > 0 && (index % stride)) { + thisCoord += step; + return; + } - const areaPoints = [[size[0], 0], [0, 0]]; - const linePoints: number[][] = []; - const step = thisShadowExtent[1] / (data.count() - 1); - let thisCoord = 0; + // FIXME + // Should consider axis.min/axis.max when drawing dataShadow. - // Optimize for large data shadow - const stride = Math.round(data.count() / size[0]); - let lastIsEmpty: boolean; - data.each([otherDim], function (value: ParsedValue, index) { - if (stride > 0 && (index % stride)) { - thisCoord += step; - return; - } + // FIXME + // 应该使用统一的空判断?还是在list里进行空判断? + const isEmpty = value == null || isNaN(value as number) || value === ''; + // See #4235. + const otherCoord = isEmpty + ? 0 : linearMap(value as number, otherDataExtent, otherShadowExtent, true); - // FIXME - // Should consider axis.min/axis.max when drawing dataShadow. + // Attempt to draw data shadow precisely when there are empty value. + if (isEmpty && !lastIsEmpty && index) { + areaPoints.push([areaPoints[areaPoints.length - 1][0], 0]); + linePoints.push([linePoints[linePoints.length - 1][0], 0]); + } + else if (!isEmpty && lastIsEmpty) { + areaPoints.push([thisCoord, 0]); + linePoints.push([thisCoord, 0]); + } - // FIXME - // 应该使用统一的空判断?还是在list里进行空判断? - const isEmpty = value == null || isNaN(value as number) || value === ''; - // See #4235. - const otherCoord = isEmpty - ? 0 : linearMap(value as number, otherDataExtent, otherShadowExtent, true); + areaPoints.push([thisCoord, otherCoord]); + linePoints.push([thisCoord, otherCoord]); - // Attempt to draw data shadow precisely when there are empty value. - if (isEmpty && !lastIsEmpty && index) { - areaPoints.push([areaPoints[areaPoints.length - 1][0], 0]); - linePoints.push([linePoints[linePoints.length - 1][0], 0]); - } - else if (!isEmpty && lastIsEmpty) { - areaPoints.push([thisCoord, 0]); - linePoints.push([thisCoord, 0]); - } + thisCoord += step; + lastIsEmpty = isEmpty; + }); - areaPoints.push([thisCoord, otherCoord]); - linePoints.push([thisCoord, otherCoord]); + polygonPts = this._shadowPolygonPts = areaPoints; + polylinePts = this._shadowPolylinePts = linePoints; - thisCoord += step; - lastIsEmpty = isEmpty; - }); + } + this._shadowData = data; + this._shadowDim = otherDim; const dataZoomModel = this.dataZoomModel; @@ -417,14 +434,14 @@ class SliderZoomView extends DataZoomView { const model = dataZoomModel.getModel(isSelectedArea ? 'selectedDataBackground' : 'dataBackground'); const group = new graphic.Group(); const polygon = new graphic.Polygon({ - shape: {points: areaPoints}, + shape: {points: polygonPts}, segmentIgnoreThreshold: 1, style: model.getModel('areaStyle').getAreaStyle(), silent: true, z2: -20 }); const polyline = new graphic.Polyline({ - shape: {points: linePoints}, + shape: {points: polylinePts}, segmentIgnoreThreshold: 1, style: model.getModel('lineStyle').getLineStyle(), silent: true, From b1eb944ae8ed0620211d14f1b78a6566f84751aa Mon Sep 17 00:00:00 2001 From: pissang Date: Tue, 16 Nov 2021 14:12:46 +0800 Subject: [PATCH 2/2] fix(throttle): clear throttle --- src/component/axisPointer/BaseAxisPointer.ts | 2 ++ src/component/dataZoom/roams.ts | 18 ++++++++++-------- src/component/parallel/ParallelView.ts | 3 ++- src/util/throttle.ts | 2 ++ 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/component/axisPointer/BaseAxisPointer.ts b/src/component/axisPointer/BaseAxisPointer.ts index 8b5ada458a..003fa28496 100644 --- a/src/component/axisPointer/BaseAxisPointer.ts +++ b/src/component/axisPointer/BaseAxisPointer.ts @@ -493,6 +493,8 @@ class BaseAxisPointer implements AxisPointer { this._handle = null; this._payloadInfo = null; } + + throttleUtil.clear(this, '_doDispatchAxisPointer'); } /** diff --git a/src/component/dataZoom/roams.ts b/src/component/dataZoom/roams.ts index 8624bb46db..c4f281d53d 100644 --- a/src/component/dataZoom/roams.ts +++ b/src/component/dataZoom/roams.ts @@ -158,14 +158,16 @@ function createCoordSysRecord(api: ExtensionAPI, coordSysModel: CoordinateSystem * This action will be throttled. */ function dispatchAction(api: ExtensionAPI, batch: DataZoomPayloadBatchItem[]) { - api.dispatchAction({ - type: 'dataZoom', - animation: { - easing: 'cubicOut', - duration: 100 - }, - batch: batch - }); + if (!api.isDisposed()) { + api.dispatchAction({ + type: 'dataZoom', + animation: { + easing: 'cubicOut', + duration: 100 + }, + batch: batch + }); + } } function containsPoint( diff --git a/src/component/parallel/ParallelView.ts b/src/component/parallel/ParallelView.ts index 88f669343f..7ba445e786 100644 --- a/src/component/parallel/ParallelView.ts +++ b/src/component/parallel/ParallelView.ts @@ -25,7 +25,7 @@ import { ElementEventName } from 'zrender/src/core/types'; import { ElementEvent } from 'zrender/src/Element'; import { ParallelAxisExpandPayload } from '../axis/parallelAxisAction'; import { each, bind, extend } from 'zrender/src/core/util'; -import { ThrottleController, createOrUpdate } from '../../util/throttle'; +import { ThrottleController, createOrUpdate, clear } from '../../util/throttle'; const CLICK_THRESHOLD = 5; // > 4 @@ -50,6 +50,7 @@ class ParallelView extends ComponentView { createOrUpdate(this, '_throttledDispatchExpand', parallelModel.get('axisExpandRate'), 'fixRate'); } dispose(ecModel: GlobalModel, api: ExtensionAPI): void { + clear(this, '_throttledDispatchExpand'); each(this._handlers, function (handler: ElementEventHandler, eventName) { api.getZr().off(eventName, handler); }); diff --git a/src/util/throttle.ts b/src/util/throttle.ts index 6581773796..653e713ab0 100755 --- a/src/util/throttle.ts +++ b/src/util/throttle.ts @@ -176,6 +176,8 @@ export function createOrUpdate( export function clear(obj: T, fnAttr: S): void { const fn = obj[fnAttr]; if (fn && (fn as any)[ORIGIN_METHOD]) { + // Clear throttle + (fn as any).clear && (fn as any).clear(); obj[fnAttr] = (fn as any)[ORIGIN_METHOD]; } }