Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: merge next branch into master #18786

Merged
merged 31 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
6f500be
test(universalTransition): a possible test file
tyn1998 Aug 5, 2022
0f85029
wip: successfully get childGroupId (a possible new interface)
tyn1998 Aug 5, 2022
4d4634f
wip: can decide "parent2child" or "child2parent"
tyn1998 Aug 6, 2022
a4cf7f9
wip: new createKeyGetters that suppot multi-drill (but only get key f…
tyn1998 Aug 6, 2022
788daec
wip: should consider getting direction from more ways & using groupId…
tyn1998 Aug 6, 2022
5063c51
refactor: rename "KeyDim" to "GroupIdDim"
tyn1998 Aug 31, 2022
01ba523
learning: add some comments
tyn1998 Sep 1, 2022
5e475a2
fix: incorrect dataGroupId for old data items in universalTransition
tyn1998 Aug 23, 2022
6e555b7
feat: multiple level drill down (alpha)
tyn1998 Sep 2, 2022
9fcbc7c
refactor: more readable universalTransition-multiLevelDrillDown.html
tyn1998 Sep 2, 2022
ce89ee7
docs: add comments for code
tyn1998 Sep 2, 2022
b123f35
docs: improve comments in test html
tyn1998 Sep 3, 2022
6168f98
docs: update universalTransition-multiLevelDrilldown.html
tyn1998 Sep 22, 2022
0c82ead
Merge pull request #17716 from apache/master
Ovilia Sep 29, 2022
e5b9be3
Merge branch 'master' into feat/multi-drill
tyn1998 Oct 15, 2022
d689521
revert changes introduced by a merge in mistake
tyn1998 Oct 15, 2022
938e415
rename `childGroupId` to `itemChildGroupId` to make it consistent wit…
tyn1998 Oct 15, 2022
8e80c01
no need to return undefined, just return
tyn1998 Oct 15, 2022
55ba825
use `!= null` to test if equal to `undefined` or `null`
tyn1998 Oct 15, 2022
ffbc6af
not use enum, just use const variables
tyn1998 Oct 15, 2022
5321c2d
merge `getGroupId` and `getChildGroupId` into one function
tyn1998 Oct 16, 2022
dc9a3f6
use createHashMap() to improve performance
tyn1998 Oct 17, 2022
6f7a395
add another test case
tyn1998 Oct 17, 2022
edd2d3c
slow down animation update in test cases
tyn1998 Oct 17, 2022
9d52862
fix unexpected appearance of xAxis.name after switching options
tyn1998 Oct 18, 2022
7a12c0b
Merge pull request #17796 from apache/master
pissang Oct 20, 2022
3d5b32c
simplify code
tyn1998 Oct 22, 2022
1dadcab
Merge pull request #17611 from tyn1998/feat/multi-drill
pissang Oct 24, 2022
7749341
Merge pull request #18049 from apache/master
plainheart Dec 10, 2022
5c1c53d
Merge pull request #18420 from apache/master
pissang Mar 27, 2023
11b4c8e
Merge pull request #18558 from apache/master
Ovilia Apr 25, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 126 additions & 48 deletions src/animation/universalTransition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,14 @@ import { EChartsExtensionInstallRegisters } from '../extension';
import { initProps } from '../util/graphic';
import DataDiffer from '../data/DataDiffer';
import SeriesData from '../data/SeriesData';
import { Dictionary, DimensionLoose, OptionDataItemObject, UniversalTransitionOption } from '../util/types';
import {
Dictionary,
DimensionLoose,
DimensionName,
DataVisualDimensions,
OptionDataItemObject,
UniversalTransitionOption
} from '../util/types';
import {
UpdateLifecycleParams,
UpdateLifecycleTransitionItem,
Expand All @@ -42,52 +49,91 @@ import Model from '../model/Model';
import Displayable from 'zrender/src/graphic/Displayable';

const DATA_COUNT_THRESHOLD = 1e4;
const TRANSITION_NONE = 0;
const TRANSITION_P2C = 1;
const TRANSITION_C2P = 2;

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

interface DiffItem {
dataGroupId: string
data: SeriesData
dim: DimensionLoose
groupId: string
childGroupId: string
divide: UniversalTransitionOption['divideShape']
dataIndex: number
}
interface TransitionSeries {
dataGroupId: string
data: SeriesData
divide: UniversalTransitionOption['divideShape']
dim?: DimensionLoose
groupIdDim?: DimensionLoose
}

function getGroupIdDimension(data: SeriesData) {
function getDimension(data: SeriesData, visualDimension: string) {
const dimensions = data.dimensions;
for (let i = 0; i < dimensions.length; i++) {
const dimInfo = data.getDimensionInfo(dimensions[i]);
if (dimInfo && dimInfo.otherDims.itemGroupId === 0) {
if (dimInfo && dimInfo.otherDims[visualDimension as keyof DataVisualDimensions] === 0) {
return dimensions[i];
}
}
}

// get value by dimension. (only get value of itemGroupId or childGroupId, so convert it to string)
function getValueByDimension(data: SeriesData, dataIndex: number, dimension: DimensionName) {
const dimInfo = data.getDimensionInfo(dimension);
const dimOrdinalMeta = dimInfo && dimInfo.ordinalMeta;
if (dimInfo) {
const value = data.get(dimInfo.name, dataIndex);
if (dimOrdinalMeta) {
return (dimOrdinalMeta.categories[value as number] as string) || value + '';
}
return value + '';
}
}

function getGroupId(data: SeriesData, dataIndex: number, dataGroupId: string, isChild: boolean) {
// try to get groupId from encode
const visualDimension = isChild ? 'itemChildGroupId' : 'itemGroupId';
const groupIdDim = getDimension(data, visualDimension);
if (groupIdDim) {
const groupId = getValueByDimension(data, dataIndex, groupIdDim);
return groupId;
}
// try to get groupId from raw data item
const rawDataItem = data.getRawDataItem(dataIndex) as OptionDataItemObject<unknown>;
const property = isChild ? 'childGroupId' : 'groupId';
if (rawDataItem && rawDataItem[property]) {
return rawDataItem[property] + '';
}
// fallback
if (isChild) {
return;
}
// try to use series.dataGroupId as groupId, otherwise use dataItem's id as groupId
return (dataGroupId || data.getId(dataIndex));
}

// flatten all data items from different serieses into one arrary
function flattenDataDiffItems(list: TransitionSeries[]) {
const items: DiffItem[] = [];

each(list, seriesInfo => {
const data = seriesInfo.data;
const dataGroupId = seriesInfo.dataGroupId;
if (data.count() > DATA_COUNT_THRESHOLD) {
if (__DEV__) {
warn('Universal transition is disabled on large data > 10k.');
}
return;
}
const indices = data.getIndices();
const groupDim = getGroupIdDimension(data);
for (let dataIndex = 0; dataIndex < indices.length; dataIndex++) {
items.push({
dataGroupId: seriesInfo.dataGroupId,
data,
dim: seriesInfo.dim || groupDim,
groupId: getGroupId(data, dataIndex, dataGroupId, false), // either of groupId or childGroupId will be used as diffItem's key,
childGroupId: getGroupId(data, dataIndex, dataGroupId, true), // depending on the transition direction (see below)
divide: seriesInfo.divide,
dataIndex
});
Expand Down Expand Up @@ -185,18 +231,71 @@ function transitionBetween(
}
}

let hasMorphAnimation = false;

function findKeyDim(items: DiffItem[]) {
for (let i = 0; i < items.length; i++) {
if (items[i].dim) {
return items[i].dim;
}
/**
* With groupId and childGroupId, we can build parent-child relationships between dataItems.
* However, we should mind the parent-child "direction" between old and new options.
*
* For example, suppose we have two dataItems from two series.data:
*
* dataA: [ dataB: [
* { {
* value: 5, value: 3,
* groupId: 'creatures', groupId: 'animals',
* childGroupId: 'animals' childGroupId: 'dogs'
* }, },
* ... ...
* ] ]
*
* where dataA is belong to optionA and dataB is belong to optionB.
*
* When we `setOption(optionB)` from optionA, we choose childGroupId of dataItemA and groupId of
* dataItemB as keys so the two keys are matched (both are 'animals'), then universalTransition
* will work. This derection is "parent -> child".
*
* If we `setOption(optionA)` from optionB, we also choose groupId of dataItemB and childGroupId
* of dataItemA as keys and universalTransition will work. This derection is "child -> parent".
*
* If there is no childGroupId specified, which means no multiLevelDrillDown/Up is needed and no
* parent-child relationship exists. This direction is "none".
*
* So we need to know whether to use groupId or childGroupId as the key when we call the keyGetter
* functions. Thus, we need to decide the direction first.
*
* The rule is:
*
* if (all childGroupIds in oldDiffItems and all groupIds in newDiffItems have common value) {
* direction = 'parent -> child';
* } else if (all groupIds in oldDiffItems and all childGroupIds in newDiffItems have common value) {
* direction = 'child -> parent';
* } else {
* direction = 'none';
* }
*/
let direction = TRANSITION_NONE;

// find all groupIds and childGroupIds from oldDiffItems
const oldGroupIds = createHashMap();
const oldChildGroupIds = createHashMap();
oldDiffItems.forEach((item) => {
item.groupId && oldGroupIds.set(item.groupId, true);
item.childGroupId && oldChildGroupIds.set(item.childGroupId, true);

});
// traverse newDiffItems and decide the direction according to the rule
for (let i = 0; i < newDiffItems.length; i++) {
const newGroupId = newDiffItems[i].groupId;
if (oldChildGroupIds.get(newGroupId)) {
direction = TRANSITION_P2C;
break;
}
const newChildGroupId = newDiffItems[i].childGroupId;
if (newChildGroupId && oldGroupIds.get(newChildGroupId)) {
direction = TRANSITION_C2P;
break;
}
}
const oldKeyDim = findKeyDim(oldDiffItems);
const newKeyDim = findKeyDim(newDiffItems);

let hasMorphAnimation = false;

function createKeyGetter(isOld: boolean, onlyGetId: boolean) {
return function (diffItem: DiffItem): string {
Expand All @@ -206,36 +305,12 @@ function transitionBetween(
if (onlyGetId) {
return data.getId(dataIndex);
}

// Use group id as transition key by default.
// So we can achieve multiple to multiple animation like drilldown / up naturally.
// If group id not exits. Use id instead. If so, only one to one transition will be applied.
const dataGroupId = diffItem.dataGroupId;

// If specified key dimension(itemGroupId by default). Use this same dimension from other data.
// PENDING: If only use key dimension of newData.
const keyDim = isOld
? (oldKeyDim || newKeyDim)
: (newKeyDim || oldKeyDim);

const dimInfo = keyDim && data.getDimensionInfo(keyDim);
const dimOrdinalMeta = dimInfo && dimInfo.ordinalMeta;

if (dimInfo) {
// Get from encode.itemGroupId.
const key = data.get(dimInfo.name, dataIndex);
if (dimOrdinalMeta) {
return dimOrdinalMeta.categories[key as number] as string || (key + '');
}
return key + '';
if (isOld) {
return direction === TRANSITION_P2C ? diffItem.childGroupId : diffItem.groupId;
}

// Get groupId from raw item. { groupId: '' }
const itemVal = data.getRawDataItem(dataIndex) as OptionDataItemObject<unknown>;
if (itemVal && itemVal.groupId) {
return itemVal.groupId + '';
else {
return direction === TRANSITION_C2P ? diffItem.childGroupId : diffItem.groupId;
}
return (dataGroupId || data.getId(dataIndex));
};
}

Expand Down Expand Up @@ -541,6 +616,7 @@ function findTransitionSeriesBatches(
}
else {
// Transition from multiple series.
// e.g. 'female', 'male' -> ['female', 'male']
if (isArray(transitionKey)) {
if (__DEV__) {
checkTransitionSeriesKeyDuplicated(transitionKeyStr);
Expand Down Expand Up @@ -569,6 +645,7 @@ function findTransitionSeriesBatches(
}
else {
// Try transition to multiple series.
// e.g. ['female', 'male'] -> 'female', 'male'
const oldData = oldDataMapForSplit.get(transitionKey);
if (oldData) {
let batch = updateBatches.get(oldData.key);
Expand Down Expand Up @@ -623,7 +700,7 @@ function transitionSeriesFromOpt(
data: globalStore.oldData[idx],
// TODO can specify divideShape in transition.
divide: getDivideShapeFromData(globalStore.oldData[idx]),
dim: finder.dimension
groupIdDim: finder.dimension
});
}
});
Expand All @@ -635,7 +712,7 @@ function transitionSeriesFromOpt(
dataGroupId: globalStore.oldDataGroupIds[idx],
data,
divide: getDivideShapeFromData(data),
dim: finder.dimension
groupIdDim: finder.dimension
});
}
});
Expand Down Expand Up @@ -665,6 +742,7 @@ export function installUniversalTransition(registers: EChartsExtensionInstallReg

// TODO multiple to multiple series.
if (globalStore.oldSeries && params.updatedSeries && params.optionChanged) {
// TODO transitionOpt was used in an old implementation and can be removed now
// Use give transition config if its' give;
const transitionOpt = params.seriesTransition;
if (transitionOpt) {
Expand Down
5 changes: 4 additions & 1 deletion src/util/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ export type DimensionLoose = DimensionName | DimensionIndexLoose;
export type DimensionType = DataStoreDimensionType;

export const VISUAL_DIMENSIONS = createHashMap<number, keyof DataVisualDimensions>([
'tooltip', 'label', 'itemName', 'itemId', 'itemGroupId', 'seriesName'
'tooltip', 'label', 'itemName', 'itemId', 'itemGroupId', 'itemChildGroupId', 'seriesName'
]);
// The key is VISUAL_DIMENSIONS
export interface DataVisualDimensions {
Expand All @@ -444,6 +444,7 @@ export interface DataVisualDimensions {
itemName?: DimensionIndex;
itemId?: DimensionIndex;
itemGroupId?: DimensionIndex;
itemChildGroupId?: DimensionIndex;
seriesName?: DimensionIndex;
}

Expand Down Expand Up @@ -618,6 +619,7 @@ export type OptionDataItemObject<T> = {
id?: OptionId;
name?: OptionName;
groupId?: OptionId;
childGroupId?: OptionId;
value?: T[] | T;
selected?: boolean;
};
Expand Down Expand Up @@ -667,6 +669,7 @@ export interface OptionEncodeVisualDimensions {
// Which is useful in prepresenting the transition key of drilldown/up animation.
// Or hover linking.
itemGroupId?: OptionEncodeValue;
childGroupdId?: OptionEncodeValue;
}
export interface OptionEncode extends OptionEncodeVisualDimensions {
[coordDim: string]: OptionEncodeValue | undefined
Expand Down
Loading