Skip to content

Commit 80172d6

Browse files
committed
Merge branch 'master' into module_default_esm
2 parents abe29f0 + 2438562 commit 80172d6

File tree

10 files changed

+981
-55
lines changed

10 files changed

+981
-55
lines changed

.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
/npm-debug.log
88
/benchmark
99
/src
10+
/ssr/client/src
1011
.DS_Store
1112
Thumbs.db
1213
Desktop.ini

src/animation/universalTransition.ts

Lines changed: 126 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,14 @@ import { EChartsExtensionInstallRegisters } from '../extension';
2828
import { initProps } from '../util/graphic';
2929
import DataDiffer from '../data/DataDiffer';
3030
import SeriesData from '../data/SeriesData';
31-
import { Dictionary, DimensionLoose, OptionDataItemObject, UniversalTransitionOption } from '../util/types';
31+
import {
32+
Dictionary,
33+
DimensionLoose,
34+
DimensionName,
35+
DataVisualDimensions,
36+
OptionDataItemObject,
37+
UniversalTransitionOption
38+
} from '../util/types';
3239
import {
3340
UpdateLifecycleParams,
3441
UpdateLifecycleTransitionItem,
@@ -42,52 +49,91 @@ import Model from '../model/Model';
4249
import Displayable from 'zrender/src/graphic/Displayable';
4350

4451
const DATA_COUNT_THRESHOLD = 1e4;
52+
const TRANSITION_NONE = 0;
53+
const TRANSITION_P2C = 1;
54+
const TRANSITION_C2P = 2;
4555

4656
interface GlobalStore { oldSeries: SeriesModel[], oldDataGroupIds: string[], oldData: SeriesData[] };
4757
const getUniversalTransitionGlobalStore = makeInner<GlobalStore, ExtensionAPI>();
4858

4959
interface DiffItem {
50-
dataGroupId: string
5160
data: SeriesData
52-
dim: DimensionLoose
61+
groupId: string
62+
childGroupId: string
5363
divide: UniversalTransitionOption['divideShape']
5464
dataIndex: number
5565
}
5666
interface TransitionSeries {
5767
dataGroupId: string
5868
data: SeriesData
5969
divide: UniversalTransitionOption['divideShape']
60-
dim?: DimensionLoose
70+
groupIdDim?: DimensionLoose
6171
}
6272

63-
function getGroupIdDimension(data: SeriesData) {
73+
function getDimension(data: SeriesData, visualDimension: string) {
6474
const dimensions = data.dimensions;
6575
for (let i = 0; i < dimensions.length; i++) {
6676
const dimInfo = data.getDimensionInfo(dimensions[i]);
67-
if (dimInfo && dimInfo.otherDims.itemGroupId === 0) {
77+
if (dimInfo && dimInfo.otherDims[visualDimension as keyof DataVisualDimensions] === 0) {
6878
return dimensions[i];
6979
}
7080
}
7181
}
7282

83+
// get value by dimension. (only get value of itemGroupId or childGroupId, so convert it to string)
84+
function getValueByDimension(data: SeriesData, dataIndex: number, dimension: DimensionName) {
85+
const dimInfo = data.getDimensionInfo(dimension);
86+
const dimOrdinalMeta = dimInfo && dimInfo.ordinalMeta;
87+
if (dimInfo) {
88+
const value = data.get(dimInfo.name, dataIndex);
89+
if (dimOrdinalMeta) {
90+
return (dimOrdinalMeta.categories[value as number] as string) || value + '';
91+
}
92+
return value + '';
93+
}
94+
}
95+
96+
function getGroupId(data: SeriesData, dataIndex: number, dataGroupId: string, isChild: boolean) {
97+
// try to get groupId from encode
98+
const visualDimension = isChild ? 'itemChildGroupId' : 'itemGroupId';
99+
const groupIdDim = getDimension(data, visualDimension);
100+
if (groupIdDim) {
101+
const groupId = getValueByDimension(data, dataIndex, groupIdDim);
102+
return groupId;
103+
}
104+
// try to get groupId from raw data item
105+
const rawDataItem = data.getRawDataItem(dataIndex) as OptionDataItemObject<unknown>;
106+
const property = isChild ? 'childGroupId' : 'groupId';
107+
if (rawDataItem && rawDataItem[property]) {
108+
return rawDataItem[property] + '';
109+
}
110+
// fallback
111+
if (isChild) {
112+
return;
113+
}
114+
// try to use series.dataGroupId as groupId, otherwise use dataItem's id as groupId
115+
return (dataGroupId || data.getId(dataIndex));
116+
}
117+
118+
// flatten all data items from different serieses into one arrary
73119
function flattenDataDiffItems(list: TransitionSeries[]) {
74120
const items: DiffItem[] = [];
75121

76122
each(list, seriesInfo => {
77123
const data = seriesInfo.data;
124+
const dataGroupId = seriesInfo.dataGroupId;
78125
if (data.count() > DATA_COUNT_THRESHOLD) {
79126
if (__DEV__) {
80127
warn('Universal transition is disabled on large data > 10k.');
81128
}
82129
return;
83130
}
84131
const indices = data.getIndices();
85-
const groupDim = getGroupIdDimension(data);
86132
for (let dataIndex = 0; dataIndex < indices.length; dataIndex++) {
87133
items.push({
88-
dataGroupId: seriesInfo.dataGroupId,
89134
data,
90-
dim: seriesInfo.dim || groupDim,
135+
groupId: getGroupId(data, dataIndex, dataGroupId, false), // either of groupId or childGroupId will be used as diffItem's key,
136+
childGroupId: getGroupId(data, dataIndex, dataGroupId, true), // depending on the transition direction (see below)
91137
divide: seriesInfo.divide,
92138
dataIndex
93139
});
@@ -185,18 +231,71 @@ function transitionBetween(
185231
}
186232
}
187233

234+
let hasMorphAnimation = false;
188235

189-
function findKeyDim(items: DiffItem[]) {
190-
for (let i = 0; i < items.length; i++) {
191-
if (items[i].dim) {
192-
return items[i].dim;
193-
}
236+
/**
237+
* With groupId and childGroupId, we can build parent-child relationships between dataItems.
238+
* However, we should mind the parent-child "direction" between old and new options.
239+
*
240+
* For example, suppose we have two dataItems from two series.data:
241+
*
242+
* dataA: [ dataB: [
243+
* { {
244+
* value: 5, value: 3,
245+
* groupId: 'creatures', groupId: 'animals',
246+
* childGroupId: 'animals' childGroupId: 'dogs'
247+
* }, },
248+
* ... ...
249+
* ] ]
250+
*
251+
* where dataA is belong to optionA and dataB is belong to optionB.
252+
*
253+
* When we `setOption(optionB)` from optionA, we choose childGroupId of dataItemA and groupId of
254+
* dataItemB as keys so the two keys are matched (both are 'animals'), then universalTransition
255+
* will work. This derection is "parent -> child".
256+
*
257+
* If we `setOption(optionA)` from optionB, we also choose groupId of dataItemB and childGroupId
258+
* of dataItemA as keys and universalTransition will work. This derection is "child -> parent".
259+
*
260+
* If there is no childGroupId specified, which means no multiLevelDrillDown/Up is needed and no
261+
* parent-child relationship exists. This direction is "none".
262+
*
263+
* So we need to know whether to use groupId or childGroupId as the key when we call the keyGetter
264+
* functions. Thus, we need to decide the direction first.
265+
*
266+
* The rule is:
267+
*
268+
* if (all childGroupIds in oldDiffItems and all groupIds in newDiffItems have common value) {
269+
* direction = 'parent -> child';
270+
* } else if (all groupIds in oldDiffItems and all childGroupIds in newDiffItems have common value) {
271+
* direction = 'child -> parent';
272+
* } else {
273+
* direction = 'none';
274+
* }
275+
*/
276+
let direction = TRANSITION_NONE;
277+
278+
// find all groupIds and childGroupIds from oldDiffItems
279+
const oldGroupIds = createHashMap();
280+
const oldChildGroupIds = createHashMap();
281+
oldDiffItems.forEach((item) => {
282+
item.groupId && oldGroupIds.set(item.groupId, true);
283+
item.childGroupId && oldChildGroupIds.set(item.childGroupId, true);
284+
285+
});
286+
// traverse newDiffItems and decide the direction according to the rule
287+
for (let i = 0; i < newDiffItems.length; i++) {
288+
const newGroupId = newDiffItems[i].groupId;
289+
if (oldChildGroupIds.get(newGroupId)) {
290+
direction = TRANSITION_P2C;
291+
break;
292+
}
293+
const newChildGroupId = newDiffItems[i].childGroupId;
294+
if (newChildGroupId && oldGroupIds.get(newChildGroupId)) {
295+
direction = TRANSITION_C2P;
296+
break;
194297
}
195298
}
196-
const oldKeyDim = findKeyDim(oldDiffItems);
197-
const newKeyDim = findKeyDim(newDiffItems);
198-
199-
let hasMorphAnimation = false;
200299

201300
function createKeyGetter(isOld: boolean, onlyGetId: boolean) {
202301
return function (diffItem: DiffItem): string {
@@ -206,36 +305,12 @@ function transitionBetween(
206305
if (onlyGetId) {
207306
return data.getId(dataIndex);
208307
}
209-
210-
// Use group id as transition key by default.
211-
// So we can achieve multiple to multiple animation like drilldown / up naturally.
212-
// If group id not exits. Use id instead. If so, only one to one transition will be applied.
213-
const dataGroupId = diffItem.dataGroupId;
214-
215-
// If specified key dimension(itemGroupId by default). Use this same dimension from other data.
216-
// PENDING: If only use key dimension of newData.
217-
const keyDim = isOld
218-
? (oldKeyDim || newKeyDim)
219-
: (newKeyDim || oldKeyDim);
220-
221-
const dimInfo = keyDim && data.getDimensionInfo(keyDim);
222-
const dimOrdinalMeta = dimInfo && dimInfo.ordinalMeta;
223-
224-
if (dimInfo) {
225-
// Get from encode.itemGroupId.
226-
const key = data.get(dimInfo.name, dataIndex);
227-
if (dimOrdinalMeta) {
228-
return dimOrdinalMeta.categories[key as number] as string || (key + '');
229-
}
230-
return key + '';
308+
if (isOld) {
309+
return direction === TRANSITION_P2C ? diffItem.childGroupId : diffItem.groupId;
231310
}
232-
233-
// Get groupId from raw item. { groupId: '' }
234-
const itemVal = data.getRawDataItem(dataIndex) as OptionDataItemObject<unknown>;
235-
if (itemVal && itemVal.groupId) {
236-
return itemVal.groupId + '';
311+
else {
312+
return direction === TRANSITION_C2P ? diffItem.childGroupId : diffItem.groupId;
237313
}
238-
return (dataGroupId || data.getId(dataIndex));
239314
};
240315
}
241316

@@ -541,6 +616,7 @@ function findTransitionSeriesBatches(
541616
}
542617
else {
543618
// Transition from multiple series.
619+
// e.g. 'female', 'male' -> ['female', 'male']
544620
if (isArray(transitionKey)) {
545621
if (__DEV__) {
546622
checkTransitionSeriesKeyDuplicated(transitionKeyStr);
@@ -569,6 +645,7 @@ function findTransitionSeriesBatches(
569645
}
570646
else {
571647
// Try transition to multiple series.
648+
// e.g. ['female', 'male'] -> 'female', 'male'
572649
const oldData = oldDataMapForSplit.get(transitionKey);
573650
if (oldData) {
574651
let batch = updateBatches.get(oldData.key);
@@ -623,7 +700,7 @@ function transitionSeriesFromOpt(
623700
data: globalStore.oldData[idx],
624701
// TODO can specify divideShape in transition.
625702
divide: getDivideShapeFromData(globalStore.oldData[idx]),
626-
dim: finder.dimension
703+
groupIdDim: finder.dimension
627704
});
628705
}
629706
});
@@ -635,7 +712,7 @@ function transitionSeriesFromOpt(
635712
dataGroupId: globalStore.oldDataGroupIds[idx],
636713
data,
637714
divide: getDivideShapeFromData(data),
638-
dim: finder.dimension
715+
groupIdDim: finder.dimension
639716
});
640717
}
641718
});
@@ -665,6 +742,7 @@ export function installUniversalTransition(registers: EChartsExtensionInstallReg
665742

666743
// TODO multiple to multiple series.
667744
if (globalStore.oldSeries && params.updatedSeries && params.optionChanged) {
745+
// TODO transitionOpt was used in an old implementation and can be removed now
668746
// Use give transition config if its' give;
669747
const transitionOpt = params.seriesTransition;
670748
if (transitionOpt) {

src/chart/bar/BaseBarSeries.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,11 +101,12 @@ class BaseBarSeriesModel<Opts extends BaseBarSeriesOption<unknown> = BaseBarSeri
101101
// If axis type is category, use tick coords instead
102102
if (axis.type === 'category' && dims != null) {
103103
const tickCoords = axis.getTicksCoords();
104+
const alignTicksWithLabel = axis.getTickModel().get('alignWithLabel');
104105

105106
let targetTickId = clampData[idx];
106107
// The index of rightmost tick of markArea is 1 larger than x1/y1 index
107108
const isEnd = dims[idx] === 'x1' || dims[idx] === 'y1';
108-
if (isEnd) {
109+
if (isEnd && !alignTicksWithLabel) {
109110
targetTickId += 1;
110111
}
111112

src/chart/graph/GraphView.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ class GraphView extends ChartView {
208208
}
209209

210210
dispose() {
211+
this.remove();
212+
211213
this._controller && this._controller.dispose();
212214
this._controllerHost = null;
213215
}
@@ -300,7 +302,11 @@ class GraphView extends ChartView {
300302
this._lineDraw.updateLayout();
301303
}
302304

303-
remove(ecModel: GlobalModel, api: ExtensionAPI) {
305+
remove() {
306+
clearTimeout(this._layoutTimeout);
307+
this._layouting = false;
308+
this._layoutTimeout = null;
309+
304310
this._symbolDraw && this._symbolDraw.remove();
305311
this._lineDraw && this._lineDraw.remove();
306312
}

src/util/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,7 @@ export type DimensionLoose = DimensionName | DimensionIndexLoose;
432432
export type DimensionType = DataStoreDimensionType;
433433

434434
export const VISUAL_DIMENSIONS = createHashMap<number, keyof DataVisualDimensions>([
435-
'tooltip', 'label', 'itemName', 'itemId', 'itemGroupId', 'seriesName'
435+
'tooltip', 'label', 'itemName', 'itemId', 'itemGroupId', 'itemChildGroupId', 'seriesName'
436436
]);
437437
// The key is VISUAL_DIMENSIONS
438438
export interface DataVisualDimensions {
@@ -444,6 +444,7 @@ export interface DataVisualDimensions {
444444
itemName?: DimensionIndex;
445445
itemId?: DimensionIndex;
446446
itemGroupId?: DimensionIndex;
447+
itemChildGroupId?: DimensionIndex;
447448
seriesName?: DimensionIndex;
448449
}
449450

@@ -618,6 +619,7 @@ export type OptionDataItemObject<T> = {
618619
id?: OptionId;
619620
name?: OptionName;
620621
groupId?: OptionId;
622+
childGroupId?: OptionId;
621623
value?: T[] | T;
622624
selected?: boolean;
623625
};
@@ -667,6 +669,7 @@ export interface OptionEncodeVisualDimensions {
667669
// Which is useful in prepresenting the transition key of drilldown/up animation.
668670
// Or hover linking.
669671
itemGroupId?: OptionEncodeValue;
672+
childGroupdId?: OptionEncodeValue;
670673
}
671674
export interface OptionEncode extends OptionEncodeVisualDimensions {
672675
[coordDim: string]: OptionEncodeValue | undefined

0 commit comments

Comments
 (0)