You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by sh...@apache.org on 2021/11/16 04:19:52 UTC

[echarts] 01/01: fix(dataZoom): not re-render shadow if not data not changed.

This is an automated email from the ASF dual-hosted git repository.

shenyi pushed a commit to branch throttle-inside-zoom
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit 94de10642da2bd5fe6b24f0edea40fcdd19834fa
Author: pissang <bm...@gmail.com>
AuthorDate: Tue Nov 16 12:18:39 2021 +0800

    fix(dataZoom): not re-render shadow if not data not changed.
    
    Tighter throttle code
---
 src/component/axisPointer/BaseAxisPointer.ts | 54 +++++++++++-----------
 src/component/brush/visualEncoding.ts        | 29 +++++-------
 src/component/dataZoom/DataZoomModel.ts      |  5 +-
 src/component/dataZoom/SliderZoomView.ts     | 56 +++++++++++++----------
 src/component/dataZoom/roams.ts              | 32 ++++++-------
 src/component/parallel/ParallelView.ts       | 30 +++++++-----
 src/util/throttle.ts                         | 68 ----------------------------
 7 files changed, 106 insertions(+), 168 deletions(-)

diff --git a/src/component/axisPointer/BaseAxisPointer.ts b/src/component/axisPointer/BaseAxisPointer.ts
index 8b5ada4..64ae646 100644
--- a/src/component/axisPointer/BaseAxisPointer.ts
+++ b/src/component/axisPointer/BaseAxisPointer.ts
@@ -123,6 +123,11 @@ class BaseAxisPointer implements AxisPointer {
     protected animationThreshold = 15;
 
     /**
+     * Throttled method.
+     */
+     _doDispatchAxisPointer: (() => void) & throttleUtil.ThrottleController;
+
+    /**
      * @implement
      */
     render(axisModel: AxisBaseModel, axisPointerModel: AxisPointerModel, api: ExtensionAPI, forceRender?: boolean) {
@@ -387,12 +392,25 @@ class BaseAxisPointer implements AxisPointer {
         handle.scaleX = handleSize[0] / 2;
         handle.scaleY = handleSize[1] / 2;
 
-        throttleUtil.createOrUpdate(
-            this,
-            '_doDispatchAxisPointer',
-            handleModel.get('throttle') || 0,
-            'fixRate'
-        );
+        this._doDispatchAxisPointer = throttleUtil.throttle(() => {
+            const handle = this._handle;
+            if (!handle) {
+                return;
+            }
+
+            const payloadInfo = this._payloadInfo;
+            const axisModel = this._axisModel;
+            this._api.dispatchAction({
+                type: 'updateAxisPointer',
+                x: payloadInfo.cursorPoint[0],
+                y: payloadInfo.cursorPoint[1],
+                tooltipOption: payloadInfo.tooltipOption,
+                axesInfo: [{
+                    axisDim: axisModel.axis.dim,
+                    axisIndex: axisModel.componentIndex
+                }]
+            });
+        }, handleModel.get('throttle') || 0);
 
         this._moveHandleToValue(value, isInit);
     }
@@ -432,28 +450,6 @@ class BaseAxisPointer implements AxisPointer {
         this._doDispatchAxisPointer();
     }
 
-    /**
-     * Throttled method.
-     */
-    _doDispatchAxisPointer() {
-        const handle = this._handle;
-        if (!handle) {
-            return;
-        }
-
-        const payloadInfo = this._payloadInfo;
-        const axisModel = this._axisModel;
-        this._api.dispatchAction({
-            type: 'updateAxisPointer',
-            x: payloadInfo.cursorPoint[0],
-            y: payloadInfo.cursorPoint[1],
-            tooltipOption: payloadInfo.tooltipOption,
-            axesInfo: [{
-                axisDim: axisModel.axis.dim,
-                axisIndex: axisModel.componentIndex
-            }]
-        });
-    }
 
     private _onHandleDragEnd() {
         this._dragging = false;
@@ -493,6 +489,8 @@ class BaseAxisPointer implements AxisPointer {
             this._handle = null;
             this._payloadInfo = null;
         }
+
+        this._doDispatchAxisPointer && this._doDispatchAxisPointer.clear();
     }
 
     /**
diff --git a/src/component/brush/visualEncoding.ts b/src/component/brush/visualEncoding.ts
index 6a221eb..0517a28 100644
--- a/src/component/brush/visualEncoding.ts
+++ b/src/component/brush/visualEncoding.ts
@@ -32,17 +32,16 @@ import SeriesModel from '../../model/Series';
 import ParallelSeriesModel from '../../chart/parallel/ParallelSeries';
 import { ZRenderType } from 'zrender/src/zrender';
 import { BrushType, BrushDimensionMinMax } from '../helper/BrushController';
+import { makeInner } from '../../util/model';
 
 type BrushVisualState = 'inBrush' | 'outOfBrush';
 
 const STATE_LIST = ['inBrush', 'outOfBrush'] as const;
-const DISPATCH_METHOD = '__ecBrushSelect' as const;
-const DISPATCH_FLAG = '__ecInBrushSelectEvent' as const;
 
-interface BrushGlobalDispatcher extends ZRenderType {
-    [DISPATCH_FLAG]: boolean;
-    [DISPATCH_METHOD]: typeof doDispatch;
-}
+const getBrushSelectDispatcher = makeInner<{
+    brushSelect: typeof doDispatch
+    inBrush: boolean
+}, ZRenderType>();
 
 interface BrushSelectedItem {
     brushId: string;
@@ -254,29 +253,25 @@ function dispatchAction(
         return;
     }
 
-    const zr = api.getZr() as BrushGlobalDispatcher;
-    if (zr[DISPATCH_FLAG]) {
+    const dispatcher = getBrushSelectDispatcher(api.getZr());
+    if (dispatcher.inBrush) {
         return;
     }
 
-    if (!zr[DISPATCH_METHOD]) {
-        zr[DISPATCH_METHOD] = doDispatch;
-    }
-
-    const fn = throttleUtil.createOrUpdate(zr, DISPATCH_METHOD, throttleDelay, throttleType);
+    dispatcher.brushSelect = throttleUtil.throttle(doDispatch, throttleDelay, throttleType === 'debounce');
 
-    fn(api, brushSelected);
+    dispatcher.brushSelect(api, brushSelected);
 }
 
 function doDispatch(api: ExtensionAPI, brushSelected: BrushSelectedItem[]): void {
     if (!api.isDisposed()) {
-        const zr = api.getZr() as BrushGlobalDispatcher;
-        zr[DISPATCH_FLAG] = true;
+        const dispatcher = getBrushSelectDispatcher(api.getZr());
+        dispatcher.inBrush = true;
         api.dispatchAction({
             type: 'brushSelect',
             batch: brushSelected
         });
-        zr[DISPATCH_FLAG] = false;
+        dispatcher.inBrush = false;
     }
 }
 
diff --git a/src/component/dataZoom/DataZoomModel.ts b/src/component/dataZoom/DataZoomModel.ts
index 6ed6ccd..086c3d9 100644
--- a/src/component/dataZoom/DataZoomModel.ts
+++ b/src/component/dataZoom/DataZoomModel.ts
@@ -391,10 +391,7 @@ class DataZoomModel<Opts extends DataZoomOption = DataZoomOption> extends Compon
             this._autoThrottle = false;
         }
         if (this._autoThrottle) {
-            const globalOption = this.ecModel.option;
-            this.option.throttle = (
-                globalOption.animation && globalOption.animationDurationUpdate > 0
-            ) ? 100 : 20;
+            this.option.throttle = this.ecModel.isAnimationEnabled() ? 100 : 20;
         }
     }
 
diff --git a/src/component/dataZoom/SliderZoomView.ts b/src/component/dataZoom/SliderZoomView.ts
index b677224..0bcce48 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,16 @@ class SliderZoomView extends DataZoomView {
         otherAxisInverse: boolean
     };
 
+    // Cached raw data. Avoid rendering data shadow multiple times.
+    private _shadowData: SeriesData;
+    private _shadowDim: string;
+
+
+    /**
+     * This action will be throttled.
+     */
+     _dispatchZoomAction: ((realtime: boolean) => void) & throttle.ThrottleController;
+
     init(ecModel: GlobalModel, api: ExtensionAPI) {
         this.api = api;
 
@@ -143,11 +154,20 @@ class SliderZoomView extends DataZoomView {
     ) {
         super.render.apply(this, arguments as any);
 
-        throttle.createOrUpdate(
-            this,
-            '_dispatchZoomAction',
-            dataZoomModel.get('throttle'),
-            'fixRate'
+        this._dispatchZoomAction = throttle.throttle(
+            (realtime: boolean) => {
+                const range = this._range;
+
+                this.api.dispatchAction({
+                    type: 'dataZoom',
+                    from: this.uid,
+                    dataZoomId: this.dataZoomModel.id,
+                    animation: realtime ? REALTIME_ANIMATION_CONFIG : null,
+                    start: range[0],
+                    end: range[1]
+                });
+            },
+            dataZoomModel.get('throttle')
         );
 
         this._orient = dataZoomModel.getOrient();
@@ -179,7 +199,7 @@ class SliderZoomView extends DataZoomView {
     }
 
     private _clear() {
-        throttle.clear(this, '_dispatchZoomAction');
+        this._dispatchZoomAction && this._dispatchZoomAction.clear();
 
         const zr = this.api.getZr();
         zr.off('mousemove', this._onBrush);
@@ -350,7 +370,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,6 +378,13 @@ class SliderZoomView extends DataZoomView {
             return;
         }
 
+        // Not re-render if data doesn't change.
+        if (data === this._shadowData && otherDim === this._shadowDim) {
+            return;
+        }
+        this._shadowData = data;
+        this._shadowDim = otherDim;
+
         let otherDataExtent = data.getDataExtent(otherDim);
         // Nice extent.
         const otherOffset = (otherDataExtent[1] - otherDataExtent[0]) * 0.3;
@@ -1014,22 +1040,6 @@ class SliderZoomView extends DataZoomView {
         });
     }
 
-    /**
-     * This action will be throttled.
-     */
-    _dispatchZoomAction(realtime: boolean) {
-        const range = this._range;
-
-        this.api.dispatchAction({
-            type: 'dataZoom',
-            from: this.uid,
-            dataZoomId: this.dataZoomModel.id,
-            animation: realtime ? REALTIME_ANIMATION_CONFIG : null,
-            start: range[0],
-            end: range[1]
-        });
-    }
-
     private _findCoordRect() {
         // Find the grid coresponding to the first axis referred by dataZoom.
         let rect: RectLike;
diff --git a/src/component/dataZoom/roams.ts b/src/component/dataZoom/roams.ts
index 8624bb4..44afb27 100644
--- a/src/component/dataZoom/roams.ts
+++ b/src/component/dataZoom/roams.ts
@@ -54,7 +54,7 @@ interface CoordSysRecord {
     // coordId: string
     controller: RoamController;
     containsPoint: (e: ZRElementEvent, x: number, y: number) => boolean;
-    dispatchAction: Curry1<typeof dispatchAction, ExtensionAPI>;
+    dispatchAction: Curry1<typeof dispatchAction, ExtensionAPI> & throttleUtil.ThrottleController;
 }
 
 
@@ -112,10 +112,9 @@ function createCoordSysRecord(api: ExtensionAPI, coordSysModel: CoordinateSystem
     const coordSysRecord: CoordSysRecord = {
         model: coordSysModel,
         containsPoint: curry(containsPoint, coordSysModel),
-        dispatchAction: curry(dispatchAction, api),
         dataZoomInfoMap: null,
         controller: null
-    };
+    } as CoordSysRecord;
 
     // Must not do anything depends on coordSysRecord outside the event handler here,
     // because coordSysRecord not completed yet.
@@ -158,14 +157,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(
@@ -283,12 +284,9 @@ export function installDataZoomRoamProcessor(registers: EChartsExtensionInstallR
 
                 controller.setPointerChecker(coordSysRecord.containsPoint);
 
-                throttleUtil.createOrUpdate(
-                    coordSysRecord,
-                    'dispatchAction',
-                    firstDzInfo.model.get('throttle', true),
-                    'fixRate'
-                );
+                coordSysRecord.dispatchAction = throttleUtil.throttle((batch) => {
+                    dispatchAction(api, batch);
+                }, firstDzInfo.model.get('throttle', true));
             });
     });
 
diff --git a/src/component/parallel/ParallelView.ts b/src/component/parallel/ParallelView.ts
index 88f6693..bb270bb 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, throttle } from '../../util/throttle';
 
 const CLICK_THRESHOLD = 5; // > 4
 
@@ -38,6 +38,14 @@ class ParallelView extends ComponentView {
     // @internal
     _mouseDownPoint: number[];
     private _handlers: Partial<Record<ElementEventName, ElementEventHandler>>;
+
+    /**
+     * @internal
+     * @param {Object} [opt] If null, cancle the last action triggering for debounce.
+     */
+    _throttledDispatchExpand: ((this: ParallelView, opt: Omit<ParallelAxisExpandPayload, 'type'>) => void)
+        & ThrottleController;
+
     render(parallelModel: ParallelModel, ecModel: GlobalModel, api: ExtensionAPI): void {
         this._model = parallelModel;
         this._api = api;
@@ -47,21 +55,22 @@ class ParallelView extends ComponentView {
                 api.getZr().on(eventName, this._handlers[eventName] = bind(handler, this) as ElementEventHandler);
             }, this);
         }
-        createOrUpdate(this, '_throttledDispatchExpand', parallelModel.get('axisExpandRate'), 'fixRate');
+
+        this._throttledDispatchExpand = throttle(
+            (opt: Omit<ParallelAxisExpandPayload, 'type'>) => {
+                this._dispatchExpand(opt);
+            },
+            parallelModel.get('axisExpandRate')
+        );
     }
     dispose(ecModel: GlobalModel, api: ExtensionAPI): void {
         each(this._handlers, function (handler: ElementEventHandler, eventName) {
             api.getZr().off(eventName, handler);
         });
         this._handlers = null;
+        this._throttledDispatchExpand && this._throttledDispatchExpand.clear();
     }
-    /**
-     * @internal
-     * @param {Object} [opt] If null, cancle the last action triggering for debounce.
-     */
-    _throttledDispatchExpand(this: ParallelView, opt: Omit<ParallelAxisExpandPayload, 'type'>): void {
-        this._dispatchExpand(opt);
-    }
+
     /**
      * @internal
      */
@@ -101,8 +110,7 @@ const handlers: Partial<Record<ElementEventName, ElementEventHandler>> = {
         const result = model.coordinateSystem.getSlidedAxisExpandWindow([e.offsetX, e.offsetY]);
         const behavior = result.behavior;
         behavior === 'jump'
-            && (this._throttledDispatchExpand as ParallelView['_throttledDispatchExpand'] & ThrottleController)
-                .debounceNextCall(model.get('axisExpandDebounce'));
+            && this._throttledDispatchExpand.debounceNextCall(model.get('axisExpandDebounce'));
         this._throttledDispatchExpand(behavior === 'none'
             ? null // Cancle the last trigger, in case that mouse slide out of the area quickly.
             : {
diff --git a/src/util/throttle.ts b/src/util/throttle.ts
index 6581773..04ac8e4 100755
--- a/src/util/throttle.ts
+++ b/src/util/throttle.ts
@@ -17,11 +17,6 @@
 * under the License.
 */
 
-
-const ORIGIN_METHOD = '\0__throttleOriginMethod' as const;
-const RATE = '\0__throttleRate' as const;
-const THROTTLE_TYPE = '\0__throttleType' as const;
-
 type ThrottleFunction = (this: unknown, ...args: unknown[]) => void;
 export type ThrottleType = 'fixRate' | 'debounce';
 
@@ -116,66 +111,3 @@ export function throttle<T extends ThrottleFunction>(
 
     return cb;
 }
-
-/**
- * Create throttle method or update throttle rate.
- *
- * @example
- * ComponentView.prototype.render = function () {
- *     ...
- *     throttle.createOrUpdate(
- *         this,
- *         '_dispatchAction',
- *         this.model.get('throttle'),
- *         'fixRate'
- *     );
- * };
- * ComponentView.prototype.remove = function () {
- *     throttle.clear(this, '_dispatchAction');
- * };
- * ComponentView.prototype.dispose = function () {
- *     throttle.clear(this, '_dispatchAction');
- * };
- *
- */
-export function createOrUpdate<T, S extends keyof T, P = T[S]>(
-    obj: T,
-    fnAttr: S,
-    rate: number,
-    throttleType: ThrottleType
-): P extends ThrottleFunction ? P & ThrottleController : never {
-    let fn = obj[fnAttr];
-
-    if (!fn) {
-        return;
-    }
-
-    const originFn = (fn as any)[ORIGIN_METHOD] || fn;
-    const lastThrottleType = (fn as any)[THROTTLE_TYPE];
-    const lastRate = (fn as any)[RATE];
-
-    if (lastRate !== rate || lastThrottleType !== throttleType) {
-        if (rate == null || !throttleType) {
-            return (obj[fnAttr] = originFn);
-        }
-
-        fn = obj[fnAttr] = throttle(
-            originFn, rate, throttleType === 'debounce'
-        );
-        (fn as any)[ORIGIN_METHOD] = originFn;
-        (fn as any)[THROTTLE_TYPE] = throttleType;
-        (fn as any)[RATE] = rate;
-    }
-
-    return fn as ReturnType<typeof createOrUpdate>;
-}
-
-/**
- * Clear throttle. Example see throttle.createOrUpdate.
- */
-export function clear<T, S extends keyof T>(obj: T, fnAttr: S): void {
-    const fn = obj[fnAttr];
-    if (fn && (fn as any)[ORIGIN_METHOD]) {
-        obj[fnAttr] = (fn as any)[ORIGIN_METHOD];
-    }
-}

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org