Skip to content

Commit

Permalink
Merge pull request #16070 from apache/optimize-dataZoom-shadow-perfor…
Browse files Browse the repository at this point in the history
…mance

[fix] dataZoom: optimize shadow render performance when roaming.
  • Loading branch information
pissang authored Nov 16, 2021
2 parents efc66bd + b1eb944 commit 2b61e57
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 56 deletions.
2 changes: 2 additions & 0 deletions src/component/axisPointer/BaseAxisPointer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,8 @@ class BaseAxisPointer implements AxisPointer {
this._handle = null;
this._payloadInfo = null;
}

throttleUtil.clear(this, '_doDispatchAxisPointer');
}

/**
Expand Down
111 changes: 64 additions & 47 deletions src/component/dataZoom/SliderZoomView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand All @@ -359,72 +365,83 @@ 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;

function createDataShadowGroup(isSelectedArea?: boolean) {
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,
Expand Down
18 changes: 10 additions & 8 deletions src/component/dataZoom/roams.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
3 changes: 2 additions & 1 deletion src/component/parallel/ParallelView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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);
});
Expand Down
2 changes: 2 additions & 0 deletions src/util/throttle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,8 @@ export function createOrUpdate<T, S extends keyof T, P = T[S]>(
export function clear<T, S extends keyof T>(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];
}
}

0 comments on commit 2b61e57

Please sign in to comment.