You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by su...@apache.org on 2020/10/23 16:26:37 UTC
[incubator-echarts] branch next updated: fix: fix custom morph for
multiple "morph" settings in one data item.
This is an automated email from the ASF dual-hosted git repository.
sushuang pushed a commit to branch next
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
The following commit(s) were added to refs/heads/next by this push:
new 9eccb26 fix: fix custom morph for multiple "morph" settings in one data item.
new b6ca47f Merge branch 'next' of github.com:apache/incubator-echarts into next
9eccb26 is described below
commit 9eccb2662f62e2b6882f5c209d86f3e27c9a60e2
Author: 100pah <su...@gmail.com>
AuthorDate: Sat Oct 24 00:15:08 2020 +0800
fix: fix custom morph for multiple "morph" settings in one data item.
---
src/chart/custom.ts | 627 ++++++++++++++++++++--------------
test/custom-shape-morphing2.html | 710 ++++++++++++++++++++-------------------
2 files changed, 728 insertions(+), 609 deletions(-)
diff --git a/src/chart/custom.ts b/src/chart/custom.ts
index 7d793dc..4d4a1a6 100644
--- a/src/chart/custom.ts
+++ b/src/chart/custom.ts
@@ -88,6 +88,7 @@ import {
combine, isInAnyMorphing, morphPath, isCombiningPath, CombineSeparateConfig, separate, CombineSeparateResult
} from 'zrender/src/tool/morphPath';
import { AnimationEasing } from 'zrender/src/animation/easing';
+import * as matrix from 'zrender/src/core/matrix';
const inner = makeInner<{
@@ -98,7 +99,8 @@ const inner = makeInner<{
// customText: string;
txConZ2Set: number;
leaveToProps: ElementProps;
- morphOption: boolean;
+ // Can morph: "morph" specified in option and el is Path.
+ canMorph: boolean;
userDuring: CustomBaseElementOption['during'];
}, Element>();
@@ -368,6 +370,8 @@ export type PrepareCustomInfo = (coordSys: CoordinateSystem) => {
api: CustomSeriesRenderItemCoordinateSystemAPI
};
+const tmpTransformable = new Transformable();
+
/**
* To reduce total package size of each coordinate systems, the modules `prepareCustom`
* of each coordinate systems are not required by each coordinate systems directly, but
@@ -469,7 +473,6 @@ class CustomSeriesView extends ChartView {
// roam or data zoom according to `actionType`.
const transOpt = customSeries.__transientTransitionOpt;
- const diffMode: DataDiffMode = transOpt ? 'multiple' : 'oneToOne';
// Enable user to disable transition animation by both set
// `from` and `to` dimension as `null`/`undefined`.
@@ -484,7 +487,8 @@ class CustomSeriesView extends ChartView {
});
}
else {
- const morphPreparation = new MorphPreparation();
+ const morphPreparation = new MorphPreparation(customSeries, transOpt);
+ const diffMode: DataDiffMode = transOpt ? 'multiple' : 'oneToOne';
(new DataDiffer(
oldData ? oldData.getIndices() : [],
@@ -504,52 +508,53 @@ class CustomSeriesView extends ChartView {
doRemoveEl(oldData.getItemGraphicEl(oldIdx), customSeries, group);
})
.update(function (newIdx, oldIdx) {
- morphPreparation.reset();
- morphPreparation.enableNextAddTo();
- const oldEl = oldData.getItemGraphicEl(oldIdx);
- const from = findMayMorphFrom(oldEl);
- morphPreparation.addFrom(from);
- morphPreparation.needCheckFromSameEl = true;
- // Do not call `removeElementDirectly` here.
- // `oldEl` is passed into the first param of `createOrUpdateItem`,
- // which will handle the remove or replace.
+ morphPreparation.reset('oneToOne');
+ let oldEl = oldData.getItemGraphicEl(oldIdx);
+ morphPreparation.findAndAddFrom(oldEl);
+
+ // PENDING:
+ // if may morph, currently we alway recreate the whole el.
+ // because if reuse some of the el in the group tree, the old el has to
+ // be removed from the group, and consequently we can not calculate
+ // the "global transition" of the old element.
+ // But is there performance issue?
+ if (morphPreparation.hasFrom()) {
+ removeElementDirectly(oldEl, group);
+ oldEl = null;
+ }
createOrUpdateItem(
oldEl, newIdx, renderItem(newIdx, payload), customSeries, group,
data, morphPreparation
);
- morphPreparation.applyMorphing('oneToOne', customSeries, transOpt);
+ morphPreparation.applyMorphing();
})
.updateManyToOne(function (newIdx, oldIndices) {
- morphPreparation.reset();
+ morphPreparation.reset('manyToOne');
for (let i = 0; i < oldIndices.length; i++) {
const oldEl = oldData.getItemGraphicEl(oldIndices[i]);
- const from = findMayMorphFrom(oldEl);
- morphPreparation.addFrom(from);
+ morphPreparation.findAndAddFrom(oldEl);
removeElementDirectly(oldEl, group);
}
- morphPreparation.enableNextAddTo();
createOrUpdateItem(
null, newIdx, renderItem(newIdx, payload), customSeries, group,
data, morphPreparation
);
- morphPreparation.applyMorphing('manyToOne', customSeries, transOpt);
+ morphPreparation.applyMorphing();
})
.updateOneToMany(function (newIndices, oldIdx) {
- morphPreparation.reset();
+ morphPreparation.reset('oneToMany');
const newLen = newIndices.length;
const oldEl = oldData.getItemGraphicEl(oldIdx);
- const from = findMayMorphFrom(oldEl);
- morphPreparation.addFrom(from);
+ morphPreparation.findAndAddFrom(oldEl);
removeElementDirectly(oldEl, group);
for (let i = 0; i < newLen; i++) {
- morphPreparation.enableNextAddTo();
createOrUpdateItem(
null, newIndices[i], renderItem(newIndices[i], payload), customSeries, group,
data, morphPreparation
);
}
- morphPreparation.applyMorphing('oneToMany', customSeries, transOpt);
+ morphPreparation.applyMorphing();
})
.execute();
}
@@ -1021,7 +1026,6 @@ function prepareTransformTransitionFrom(
isInit: boolean
): void {
const enterFrom = elOption.enterFrom;
- const fromEl = morphFromEl || el;
if (isInit && enterFrom) {
const enterFromKeys = keys(enterFrom);
for (let i = 0; i < enterFromKeys.length; i++) {
@@ -1038,13 +1042,14 @@ function prepareTransformTransitionFrom(
// If morphing, force transition all transform props.
// otherwise might have incorrect morphing animation.
if (morphFromEl) {
- setTransformPropToTransitionFrom(transFromProps, 'x', fromEl);
- setTransformPropToTransitionFrom(transFromProps, 'y', fromEl);
- setTransformPropToTransitionFrom(transFromProps, 'scaleX', fromEl);
- setTransformPropToTransitionFrom(transFromProps, 'scaleY', fromEl);
- setTransformPropToTransitionFrom(transFromProps, 'originX', fromEl);
- setTransformPropToTransitionFrom(transFromProps, 'originY', fromEl);
- setTransformPropToTransitionFrom(transFromProps, 'rotation', fromEl);
+ const fromTransformable = calcOldElLocalTransformBasedOnNewElParent(morphFromEl, el);
+ setTransformPropToTransitionFrom(transFromProps, 'x', fromTransformable);
+ setTransformPropToTransitionFrom(transFromProps, 'y', fromTransformable);
+ setTransformPropToTransitionFrom(transFromProps, 'scaleX', fromTransformable);
+ setTransformPropToTransitionFrom(transFromProps, 'scaleY', fromTransformable);
+ setTransformPropToTransitionFrom(transFromProps, 'originX', fromTransformable);
+ setTransformPropToTransitionFrom(transFromProps, 'originY', fromTransformable);
+ setTransformPropToTransitionFrom(transFromProps, 'rotation', fromTransformable);
}
else if (elOption.transition) {
const transitionKeys = normalizeToArray(elOption.transition);
@@ -1053,7 +1058,7 @@ function prepareTransformTransitionFrom(
if (key === 'style' || key === 'shape' || key === 'extra') {
continue;
}
- const elVal = fromEl[key];
+ const elVal = el[key];
if (__DEV__) {
checkTransformPropRefer(key, 'el.transition');
checkNonStyleTansitionRefer(key, elOption[key], elVal);
@@ -1064,8 +1069,8 @@ function prepareTransformTransitionFrom(
}
// This default transition see [STRATEGY_TRANSITION]
else {
- setTransformPropToTransitionFrom(transFromProps, 'x', fromEl);
- setTransformPropToTransitionFrom(transFromProps, 'y', fromEl);
+ setTransformPropToTransitionFrom(transFromProps, 'x', el);
+ setTransformPropToTransitionFrom(transFromProps, 'y', el);
}
}
@@ -1173,6 +1178,54 @@ function prepareStyleTransitionFrom(
}
}
+/**
+ * If make "transform"(x/y/scaleX/scaleY/orient/originX/originY) transition between
+ * two path elements that have different hierarchy, before we retrieve the "from" props,
+ * we have to calculate the local transition of the "oldPath" based on the parent of
+ * the "newPath".
+ * At present, the case only happend in "morphing". Without morphing, the transform
+ * transition are all between elements in the same hierarchy, where this kind of process
+ * is not needed.
+ *
+ * [CAVEAT]:
+ * This method makes sense only if: (very tricky)
+ * (1) "newEl" has been added to its final parent.
+ * (2) Local transform props of "newPath.parent" are not at their final value but already
+ * have been at the "from value".
+ * This is currently ensured by:
+ * (2.1) "graphicUtil.animationFrom", which will set the element to the "from value"
+ * immediately.
+ * (2.2) "morph" option is not allowed to be set on Group, so all of the groups have
+ * been finished their "updateElNormal" when calling this method in morphing process.
+ */
+function calcOldElLocalTransformBasedOnNewElParent(oldEl: Element, newEl: Element): Transformable {
+ if (!oldEl || oldEl === newEl || oldEl.parent === newEl.parent) {
+ return oldEl;
+ }
+
+ // Not sure oldEl is rendered (may have "lazyUpdate"),
+ // so always call `getComputedTransform`.
+ const tmpM = tmpTransformable.transform
+ || (tmpTransformable.transform = matrix.identity([]));
+
+ const oldGlobalTransform = oldEl.getComputedTransform();
+ oldGlobalTransform
+ ? matrix.copy(tmpM, oldGlobalTransform)
+ : matrix.identity(tmpM);
+
+ const newParent = newEl.parent;
+ if (newParent) {
+ newParent.getComputedTransform();
+ }
+
+ tmpTransformable.originX = oldEl.originX;
+ tmpTransformable.originY = oldEl.originY;
+ tmpTransformable.parent = newParent;
+ tmpTransformable.decomposeTransform();
+
+ return tmpTransformable;
+}
+
let checkNonStyleTansitionRefer: (propName: string, optVal: unknown, elVal: unknown) => void;
if (__DEV__) {
checkNonStyleTansitionRefer = function (propName: string, optVal: unknown, elVal: unknown): void {
@@ -1447,14 +1500,14 @@ function setLagecyTransformProp(
elOption: CustomElementOption,
targetProps: Partial<Pick<Transformable, TransformProp>>,
legacyName: LegacyTransformProp,
- fromEl?: Element // If provided, retrieve from the element.
+ fromTransformable?: Transformable // If provided, retrieve from the element.
): void {
const legacyArr = (elOption as any)[legacyName];
const xyName = LEGACY_TRANSFORM_PROPS[legacyName];
if (legacyArr) {
- if (fromEl) {
- targetProps[xyName[0]] = fromEl[xyName[0]];
- targetProps[xyName[1]] = fromEl[xyName[1]];
+ if (fromTransformable) {
+ targetProps[xyName[0]] = fromTransformable[xyName[0]];
+ targetProps[xyName[1]] = fromTransformable[xyName[1]];
}
else {
targetProps[xyName[0]] = legacyArr[0];
@@ -1467,20 +1520,20 @@ function setTransformProp(
elOption: CustomElementOption,
allProps: Partial<Pick<Transformable, TransformProp>>,
name: TransformProp,
- fromEl?: Element // If provided, retrieve from the element.
+ fromTransformable?: Transformable // If provided, retrieve from the element.
): void {
if (elOption[name] != null) {
- allProps[name] = fromEl ? fromEl[name] : elOption[name];
+ allProps[name] = fromTransformable ? fromTransformable[name] : elOption[name];
}
}
function setTransformPropToTransitionFrom(
transitionFrom: Partial<Pick<Transformable, TransformProp>>,
name: TransformProp,
- fromEl?: Element // If provided, retrieve from the element.
+ fromTransformable?: Transformable // If provided, retrieve from the element.
): void {
- if (fromEl) {
- transitionFrom[name] = fromEl[name];
+ if (fromTransformable) {
+ transitionFrom[name] = fromTransformable[name];
}
}
@@ -1847,23 +1900,16 @@ function doCreateOrUpdateEl(
assert(elOption, 'should not have an null/undefined element setting');
}
- const optionMorph = (elOption as CustomZRPathOption).morph;
- if (optionMorph && morphPreparation) {
- morphPreparation.meetMorphOption();
- }
-
let toBeReplacedIdx = -1;
if (
el && (
doesElNeedRecreate(el, elOption)
- || (
- // el has been used as the "from" for other el.
- // So can not be used as the old el in this place.
- morphPreparation
- && morphPreparation.needCheckFromSameEl
- && morphPreparation.getSingleFrom() === el
- && !optionMorph
- )
+ // || (
+ // // PENDING: even in one-to-one mapping case, if el is marked as morph,
+ // // do not sure whether the el will be mapped to another el with different
+ // // hierarchy in Group tree. So always recreate el rather than reuse the el.
+ // morphPreparation && morphPreparation.isOneToOneFrom(el)
+ // )
)
) {
// Should keep at the original index, otherwise "merge by index" will be incorrect.
@@ -1883,8 +1929,8 @@ function doCreateOrUpdateEl(
el.clearStates();
}
- inner(el).morphOption = optionMorph;
- const thisElIsMorphTo = optionMorph && isPath(el) && morphPreparation && morphPreparation.hasFrom();
+ const canMorph = inner(el).canMorph = (elOption as CustomZRPathOption).morph && isPath(el);
+ const thisElIsMorphTo = canMorph && morphPreparation && morphPreparation.hasFrom();
// Use update animation when morph is enabled.
const isInit = elIsNewCreated && !thisElIsMorphTo;
@@ -2312,42 +2358,62 @@ function removeElementDirectly(el: Element, group: ViewRootGroup): void {
}
+type MorphPreparationType = 'oneToOne' | 'oneToMany' | 'manyToOne';
+
+/**
+ * Any morph-potential el should added by `morphPreparation.addTo(el)`.
+ * And they may apply morph or not when `morphPreparation.applyMorphing()`.
+ * But at least, all of the "to" elements will apply all of the updates
+ * as `doCreateOrUpdateItem` did.
+ */
class MorphPreparation {
+ private _type: MorphPreparationType;
private _fromList: graphicUtil.Path[] = [];
private _toList: graphicUtil.Path[] = [];
private _toElOptionList: CustomElementOption[] = [];
private _allPropsFinalList: ElementProps[] = [];
private _toDataIndices: number[] = [];
- private _preventAddTo: boolean;
- private _metMorphOption: boolean;
- needCheckFromSameEl: boolean;
+ private _transOpt: SeriesModel['__transientTransitionOpt'];
+ private _seriesModel: CustomSeriesModel;
+ // Key: `toDataIndex`, not `toIdx`
+ private _morphConfigList: CombineSeparateConfig[] = [];
- hasFrom(): boolean {
- return !!this._fromList.length;
+ constructor(
+ seriesModel: CustomSeriesModel,
+ transOpt: SeriesModel['__transientTransitionOpt']
+ ) {
+ this._seriesModel = seriesModel;
+ this._transOpt = transOpt;
}
- getSingleFrom(): graphicUtil.Path {
- return this._fromList[0];
+ hasFrom(): boolean {
+ return !!this._fromList.length;
}
- addFrom(path: graphicUtil.Path): void {
- path && this._fromList.push(path);
- }
+ // isOneToOneFrom(el: Element): boolean {
+ // if (el && inner(el).canMorph) {
+ // const fromList = this._fromList;
+ // for (let i = 0; i < fromList.length; i++) {
+ // if (fromList[i] === el) {
+ // return true;
+ // }
+ // }
+ // }
+ // }
- meetMorphOption() {
- let errMsg = '';
- if (this._metMorphOption) {
- if (__DEV__) {
- errMsg = 'Only one "morph" allowed in each `renderItem` return.';
+ findAndAddFrom(el: Element): void {
+ if (!el) {
+ return;
+ }
+ if (inner(el).canMorph) {
+ this._fromList.push(el as graphicUtil.Path);
+ }
+ if (el.isGroup) {
+ const children = (el as graphicUtil.Group).childrenRef();
+ for (let i = 0; i < children.length; i++) {
+ this.findAndAddFrom(children[i]);
}
- throwError(errMsg);
}
- this._metMorphOption = true;
- }
-
- enableNextAddTo(): void {
- this._metMorphOption = false;
- this._preventAddTo = false;
}
addTo(
@@ -2356,36 +2422,15 @@ class MorphPreparation {
dataIndex: number,
allPropsFinal: ElementProps
): void {
- // After added successfully, can not perform a next add until `enableNextAddTo` called.
- // At present only one 'morph' option works if multiple 'morph` option sepecified for a data item.
- if (!this._preventAddTo && path) {
+ if (path) {
this._toList.push(path);
this._toElOptionList.push(elOption);
this._toDataIndices.push(dataIndex);
this._allPropsFinalList.push(allPropsFinal);
- this._preventAddTo = true;
}
}
- applyMorphing(
- type: 'oneToOne' | 'oneToMany' | 'manyToOne',
- seriesModel: CustomSeriesModel,
- transOpt: SeriesModel['__transientTransitionOpt'] // May be null/undefined
- ): void {
- if (!seriesModel.isAnimationEnabled()) {
- return;
- }
- const fromList = this._fromList;
- const toList = this._toList;
- const toElOptionList = this._toElOptionList;
- const toDataIndices = this._toDataIndices;
- const allPropsFinalList = this._allPropsFinalList;
- if (!fromList.length || !toList.length) {
- return;
- }
- const elAnimationConfig = prepareMorphElementAnimateConfig(seriesModel, toDataIndices[0], transOpt);
- const morphDuration = !!elAnimationConfig.duration;
-
+ applyMorphing(): void {
// [MORPHING_LOGIC_HINT]
// Pay attention to the order:
// (A) Apply `allPropsFinal` and `styleOption` to "to".
@@ -2410,181 +2455,247 @@ class MorphPreparation {
// [MORPHING_LOGIC_HINT]
// Make sure `applyPropsFinal` always be called for "to".
- if (type === 'oneToOne') {
- const from = fromList[0];
- const to = toList[0];
- const toElOption = toElOptionList[0];
- const toDataIndex = toDataIndices[0];
- const allPropsFinal = allPropsFinalList[0];
-
- if (!isCombiningPath(from)) {
- const shouldMorph = morphDuration
- // from === to usually happen in scenarios where internal update like
- // "dataZoom", "legendToggle" happen. If from is not in any morphing,
- // we do not need to call `morphPath`.
- && (from !== to || isInAnyMorphing(from));
- const morphFrom = shouldMorph ? from : null;
-
- // See [Case_II] above.
- // In this case, there is probably `from === to`. And the `transitionFromProps` collecting
- // does not depends on morphing. So we collect `transitionFromProps` first.
- const transFromProps = {} as ElementProps;
- prepareShapeOrExtraTransitionFrom('shape', to, morphFrom, toElOption, transFromProps, false);
- prepareShapeOrExtraTransitionFrom('extra', to, morphFrom, toElOption, transFromProps, false);
- prepareTransformTransitionFrom(to, morphFrom, toElOption, transFromProps, false);
- prepareStyleTransitionFrom(to, morphFrom, toElOption, toElOption.style, transFromProps, false);
-
- applyPropsFinal(to, allPropsFinal, toElOption.style);
-
- if (shouldMorph) {
- morphPath(from, to, elAnimationConfig);
- }
- applyTransitionFrom(to, toDataIndex, toElOption, seriesModel, transFromProps, false);
- }
- else {
- applyPropsFinal(to, allPropsFinal, toElOption.style);
+ const type = this._type;
+ const fromList = this._fromList;
+ const toList = this._toList;
+ const toListLen = toList.length;
+ const fromListLen = fromList.length;
- if (morphDuration) {
- const combineResult = combine([from], to, elAnimationConfig, copyPropsWhenDivided);
- processCombineSeparateIndividuals(
- 'combine', combineResult, seriesModel, toElOption, toDataIndex
- );
- }
- // The target el will not be displayed and transition from multiple path.
- // transition on the target el does not make sense.
+ if (!fromListLen || !toListLen) {
+ return;
+ }
+
+ if (type === 'oneToOne') {
+ // In one-to-one case, we by default apply a simple rule:
+ // map "from" and "to" one by one.
+ // For this case: old_data_item_el and new_data_item_el
+ // has the same hierarchy of group tree but only some path type changed.
+ for (let toIdx = 0; toIdx < toListLen; toIdx++) {
+ this._oneToOneForSingleTo(toIdx, toIdx);
}
}
else if (type === 'manyToOne') {
- const to = toList[0];
- const toElOption = toElOptionList[0];
- const toDataIndex = toDataIndices[0];
- const allPropsFinal = allPropsFinalList[0];
-
- applyPropsFinal(to, allPropsFinal, toElOption.style);
-
- if (morphDuration) {
- const combineResult = combine(fromList, to, elAnimationConfig, copyPropsWhenDivided);
- processCombineSeparateIndividuals(
- 'combine', combineResult, seriesModel, toElOption, toDataIndex
+ // A rough strategy: if there are more than one "to", we simply divide "fromList" equally.
+ const fromSingleSegLen = Math.max(1, Math.floor(fromListLen / toListLen));
+ for (
+ let toIdx = 0, fromIdxStart = 0;
+ toIdx < toListLen;
+ toIdx++, fromIdxStart += fromSingleSegLen
+ ) {
+ const fromCount = toIdx + 1 >= toListLen
+ ? fromListLen - fromIdxStart
+ : fromSingleSegLen;
+ this._manyToOneForSingleTo(
+ toIdx, fromIdxStart >= fromListLen ? null : fromIdxStart, fromCount
);
}
}
else if (type === 'oneToMany') {
- const from = fromList[0];
- for (let i = 0; i < toList.length; i++) {
- applyPropsFinal(toList[i], allPropsFinalList[i], toElOptionList[i].style);
+ // A rough strategy: if there are more than one "from", we simply divide "toList" equally.
+ const toSingleSegLen = Math.max(1, Math.floor(toListLen / fromListLen));
+ for (
+ let toIdxStart = 0, fromIdx = 0;
+ toIdxStart < toListLen;
+ toIdxStart += toSingleSegLen, fromIdx++
+ ) {
+ const toCount = toIdxStart + toSingleSegLen >= toListLen
+ ? toListLen - toIdxStart
+ : toSingleSegLen;
+ this._oneToManyForSingleFrom(
+ toIdxStart, toCount, fromIdx >= fromListLen ? null : fromIdx
+ );
}
+ }
+ }
+
+ private _oneToOneForSingleTo(
+ // "to" must NOT be null/undefined.
+ toIdx: number,
+ // May `fromIdx >= this._fromList.length`
+ fromIdx: number
+ ): void {
+ const to = this._toList[toIdx];
+ const toElOption = this._toElOptionList[toIdx];
+ const toDataIndex = this._toDataIndices[toIdx];
+ const allPropsFinal = this._allPropsFinalList[toIdx];
+ const from = this._fromList[fromIdx];
+
+ const elAnimationConfig = this._getOrCreateMorphConfig(toDataIndex);
+ const morphDuration = elAnimationConfig.duration;
+
+ if (from && isCombiningPath(from)) {
+ applyPropsFinal(to, allPropsFinal, toElOption.style);
+
if (morphDuration) {
- const separateResult = separate(from, toList, elAnimationConfig, copyPropsWhenDivided);
- processCombineSeparateIndividuals(
- 'separate', separateResult, seriesModel, toElOptionList, toDataIndices
- );
+ const combineResult = combine([from], to, elAnimationConfig, copyPropsWhenDivided);
+ this._processResultIndividuals(combineResult, toIdx, null);
}
+ // The target el will not be displayed and transition from multiple path.
+ // transition on the target el does not make sense.
}
+ else {
+ const morphFrom = (
+ morphDuration
+ // from === to usually happen in scenarios where internal update like
+ // "dataZoom", "legendToggle" happen. If from is not in any morphing,
+ // we do not need to call `morphPath`.
+ && from
+ && (from !== to || isInAnyMorphing(from))
+ ) ? from : null;
+
+ // See [Case_II] above.
+ // In this case, there is probably `from === to`. And the `transitionFromProps` collecting
+ // does not depends on morphing. So we collect `transitionFromProps` first.
+ const transFromProps = {} as ElementProps;
+ prepareShapeOrExtraTransitionFrom('shape', to, morphFrom, toElOption, transFromProps, false);
+ prepareShapeOrExtraTransitionFrom('extra', to, morphFrom, toElOption, transFromProps, false);
+ prepareTransformTransitionFrom(to, morphFrom, toElOption, transFromProps, false);
+ prepareStyleTransitionFrom(to, morphFrom, toElOption, toElOption.style, transFromProps, false);
- }
+ applyPropsFinal(to, allPropsFinal, toElOption.style);
- reset(): void {
- this._preventAddTo = true;
- this.needCheckFromSameEl = false;
- this._metMorphOption = false;
- this._fromList.length =
- this._toList.length =
- this._toElOptionList.length =
- this._allPropsFinalList.length =
- this._toDataIndices.length = 0;
+ if (morphFrom) {
+ morphPath(morphFrom, to, elAnimationConfig);
+ }
+ applyTransitionFrom(to, toDataIndex, toElOption, this._seriesModel, transFromProps, false);
+ }
}
-}
-function processCombineSeparateIndividuals<TYPE extends 'combine' | 'separate'>(
- type: TYPE,
- combineSeparateResult: CombineSeparateResult,
- seriesModel: CustomSeriesModel,
- toElOptionInput: TYPE extends 'separate' ? CustomElementOption[] : CustomElementOption,
- dataIndexInput: TYPE extends 'separate' ? number[] : number
-): void {
- const isSeparate = type === 'separate';
-
- for (let i = 0; i < combineSeparateResult.count; i++) {
- const fromIndividual = combineSeparateResult.fromIndividuals[i];
- const toIndividual = combineSeparateResult.toIndividuals[i];
-
- const toElOption = isSeparate
- ? (toElOptionInput as CustomElementOption[])[i]
- : (toElOptionInput as CustomElementOption);
- const dataIndex = isSeparate
- ? (dataIndexInput as number[])[i]
- : (dataIndexInput as number);
-
- const transFromProps = {} as ElementProps;
- prepareTransformTransitionFrom(
- toIndividual, fromIndividual, toElOption, transFromProps, false
- );
- prepareStyleTransitionFrom(
- toIndividual, fromIndividual, toElOption, toElOption.style, transFromProps, false
- );
- applyTransitionFrom(
- toIndividual, dataIndex, toElOption, seriesModel, transFromProps, false
- );
+ private _manyToOneForSingleTo(
+ // "to" must NOT be null/undefined.
+ toIdx: number,
+ // May be null.
+ fromIdxStart: number,
+ fromCount: number
+ ): void {
+ const to = this._toList[toIdx];
+ const toElOption = this._toElOptionList[toIdx];
+ const allPropsFinal = this._allPropsFinalList[toIdx];
+
+ applyPropsFinal(to, allPropsFinal, toElOption.style);
+
+ const elAnimationConfig = this._getOrCreateMorphConfig(this._toDataIndices[toIdx]);
+ if (elAnimationConfig.duration && fromIdxStart != null) {
+ const combineFromList = [];
+ for (let fromIdx = fromIdxStart; fromIdx < fromCount; fromIdx++) {
+ combineFromList.push(this._fromList[fromIdx]);
+ }
+ const combineResult = combine(combineFromList, to, elAnimationConfig, copyPropsWhenDivided);
+ this._processResultIndividuals(combineResult, toIdx, null);
+ }
}
-}
-// At present only one 'morph' enabled in each data item.
-function findMayMorphFrom(el: Element): graphicUtil.Path {
- if (!el) {
- return;
+ private _oneToManyForSingleFrom(
+ // "to" must NOT be null/undefined.
+ toIdxStart: number,
+ toCount: number,
+ // May be null
+ fromIdx: number
+ ): void {
+ const from = fromIdx == null ? null : this._fromList[fromIdx];
+ const toList = this._toList;
+
+ const separateToList = [];
+ for (let toIdx = toIdxStart; toIdx < toCount; toIdx++) {
+ const to = toList[toIdx];
+ applyPropsFinal(to, this._allPropsFinalList[toIdx], this._toElOptionList[toIdx].style);
+ separateToList.push(to);
+ }
+
+ const elAnimationConfig = this._getOrCreateMorphConfig(this._toDataIndices[toIdxStart]);
+ if (elAnimationConfig.duration && from) {
+ const separateResult = separate(from, separateToList, elAnimationConfig, copyPropsWhenDivided);
+ this._processResultIndividuals(separateResult, toIdxStart, toCount);
+ }
}
- if (inner(el).morphOption && isPath(el)) {
- return el;
+
+ private _processResultIndividuals(
+ combineSeparateResult: CombineSeparateResult,
+ toIdxStart: number,
+ toCount: number
+ ): void {
+ const isSeparate = toCount != null;
+
+ for (let i = 0; i < combineSeparateResult.count; i++) {
+ const fromIndividual = combineSeparateResult.fromIndividuals[i];
+ const toIndividual = combineSeparateResult.toIndividuals[i];
+ // Here it's a trick:
+ // For "combine" case, all of the `toIndividuals` map to the same `toIdx`.
+ // For "separate" case, the `toIndividuals` map to some certain segment of `_toList` accurately.
+ const toIdx = toIdxStart + (isSeparate ? i : 0);
+
+ const toElOption = this._toElOptionList[toIdx];
+ const dataIndex = this._toDataIndices[toIdx];
+
+ const transFromProps = {} as ElementProps;
+ prepareTransformTransitionFrom(
+ toIndividual, fromIndividual, toElOption, transFromProps, false
+ );
+ prepareStyleTransitionFrom(
+ toIndividual, fromIndividual, toElOption, toElOption.style, transFromProps, false
+ );
+ applyTransitionFrom(
+ toIndividual, dataIndex, toElOption, this._seriesModel, transFromProps, false
+ );
+ }
}
- if (el.isGroup) {
- const elGroup = el as graphicUtil.Group;
- const children = elGroup.childrenRef();
- for (let i = 0; i < children.length; i++) {
- const path = findMayMorphFrom(children[i]);
- if (path) {
- return path;
+
+ _getOrCreateMorphConfig(dataIndex: number): CombineSeparateConfig {
+ const morphConfigList = this._morphConfigList;
+ let config = morphConfigList[dataIndex];
+ if (config) {
+ return config;
+ }
+
+ let duration: number;
+ let easing: AnimationEasing;
+ let delay: number;
+ const seriesModel = this._seriesModel;
+ const transOpt = this._transOpt;
+
+ if (seriesModel.isAnimationEnabled()) {
+ // PENDING: refactor? this is the same logic as `src/util/graphic.ts#animateOrSetProps`.
+ let animationPayload: PayloadAnimationPart;
+ if (seriesModel && seriesModel.ecModel) {
+ const updatePayload = seriesModel.ecModel.getUpdatePayload();
+ animationPayload = (updatePayload && updatePayload.animation) as PayloadAnimationPart;
+ }
+ if (animationPayload) {
+ duration = animationPayload.duration || 0;
+ easing = animationPayload.easing || 'cubicOut';
+ delay = animationPayload.delay || 0;
+ }
+ else {
+ easing = seriesModel.get('animationEasingUpdate');
+ const delayOption = seriesModel.get('animationDelayUpdate');
+ delay = isFunction(delayOption) ? delayOption(dataIndex) : delayOption;
+ const durationOption = seriesModel.get('animationDurationUpdate');
+ duration = isFunction(durationOption) ? durationOption(dataIndex) : durationOption;
}
}
+
+ config = {
+ duration: duration || 0,
+ delay: delay,
+ easing: easing,
+ dividingMethod: transOpt ? transOpt.dividingMethod : null
+ };
+ morphConfigList[dataIndex] = config;
+
+ return config;
}
-}
-function prepareMorphElementAnimateConfig(
- seriesModel: CustomSeriesModel,
- dataIndex: number,
- transOpt: SeriesModel['__transientTransitionOpt'] // may be null/undefined
-): CombineSeparateConfig {
- let duration: number;
- let easing: AnimationEasing;
- let delay: number;
-
- // PENDING: refactor? this is the same logic as `src/util/graphic.ts#animateOrSetProps`.
- let animationPayload: PayloadAnimationPart;
- if (seriesModel && seriesModel.ecModel) {
- const updatePayload = seriesModel.ecModel.getUpdatePayload();
- animationPayload = (updatePayload && updatePayload.animation) as PayloadAnimationPart;
- }
- if (animationPayload) {
- duration = animationPayload.duration || 0;
- easing = animationPayload.easing || 'cubicOut';
- delay = animationPayload.delay || 0;
+ reset(type: MorphPreparationType): void {
+ // `this._morphConfigList` can be kept. It only related to `dataIndex`.
+ this._type = type;
+ this._fromList.length =
+ this._toList.length =
+ this._toElOptionList.length =
+ this._allPropsFinalList.length =
+ this._toDataIndices.length = 0;
}
- else {
- easing = seriesModel.get('animationEasingUpdate');
- const delayOption = seriesModel.get('animationDelayUpdate');
- delay = isFunction(delayOption) ? delayOption(dataIndex) : delayOption;
- const durationOption = seriesModel.get('animationDurationUpdate');
- duration = isFunction(durationOption) ? durationOption(dataIndex) : durationOption;
- }
-
- return {
- duration: duration || 0,
- delay: delay,
- easing: easing,
- dividingMethod: transOpt ? transOpt.dividingMethod : null
- };
}
function copyPropsWhenDivided(
@@ -2592,12 +2703,14 @@ function copyPropsWhenDivided(
tarPath: graphicUtil.Path,
willClone: boolean
): void {
- tarPath.x = srcPath.x;
- tarPath.y = srcPath.y;
- tarPath.scaleX = srcPath.scaleX;
- tarPath.scaleY = srcPath.scaleY;
- tarPath.originX = srcPath.originX;
- tarPath.originY = srcPath.originY;
+ // Do not copy transform props.
+ // Sub paths are transfrom based on their host path.
+ // tarPath.x = srcPath.x;
+ // tarPath.y = srcPath.y;
+ // tarPath.scaleX = srcPath.scaleX;
+ // tarPath.scaleY = srcPath.scaleY;
+ // tarPath.originX = srcPath.originX;
+ // tarPath.originY = srcPath.originY;
// If just carry the style, will not be modifed, so do not copy.
tarPath.style = willClone
diff --git a/test/custom-shape-morphing2.html b/test/custom-shape-morphing2.html
index 28c95ec..ac12738 100644
--- a/test/custom-shape-morphing2.html
+++ b/test/custom-shape-morphing2.html
@@ -92,19 +92,10 @@ under the License.
'#c23531', '#2f4554', '#61a0a8', '#d48265', '#91c7ae', '#749f83',
'#ca8622', '#bda29a', '#6e7074', '#546570', '#c4ccd3'
];
- const zTagColorMap = {};
- let zTagColorIndex = 0;
- function getZTagColor(zTag) {
- if (!zTagColorMap[zTag]) {
- zTagColorMap[zTag] = Z_TAG_COLORS[zTagColorIndex];
- zTagColorIndex++;
- if (zTagColorIndex >= Z_TAG_COLORS.length) {
- zTagColorIndex = 0;
- }
- }
- return zTagColorMap[zTag]
- }
-
+ const Z_TAG_COLORS_2 = [
+ '#51689b', '#ce5c5c', '#fbc357', '#8fbf8f', '#659d84', '#fb8e6a', '#c77288', '#786090',
+ '#91c4c5', '#6890ba'
+ ];
const SYMBOL_PATHS = [
'path://m67.25,28.9c27.42,-69.1 134.84,0 0,88.85c-134.84,-88.85 -27.42,-157.96 0,-88.85z',
'path://M16 0c-8.837 0-16 7.163-16 16s7.163 16 16 16 16-7.163 16-16-7.163-16-16-16zM22 8c1.105 0 2 1.343 2 3s-0.895 3-2 3-2-1.343-2-3 0.895-3 2-3zM10 8c1.105 0 2 1.343 2 3s-0.895 3-2 3-2-1.343-2-3 0.895-3 2-3zM16 28c-5.215 0-9.544-4.371-10-9.947 2.93 1.691 6.377 2.658 10 2.658s7.070-0.963 10-2.654c-0.455 5.576-4.785 9.942-10 9.942z',
@@ -113,25 +104,10 @@ under the License.
'path://M237.062,81.761L237.062,81.761c-12.144-14.24-25.701-20.1-40.68-19.072 c-10.843,0.747-20.938,5.154-30.257,13.127c-9.51-5.843-19.8-9.227-30.859-10.366c0.521-3.197,1.46-6.306,2.85-9.363 c3.458-7.038,8.907-12.741,16.331-17.296c-5.609-3.384-11.227-6.799-16.854-10.279c-16.257,8.104-25.06,20.601-26.463,38.417 c-7.599,1.705-14.685,4.486-21.247,8.437c-9.164-7.677-18.996-11.917-29.496-12.632c-14.819-0.998-28.467,4.787-40.938,18.827 C6.445,96.182,0,114.867,0,136.242c-0.0 [...]
'path://M237.062,81.761L237.062,81.761c-12.144-14.24-25.701-20.1-40.68-19.072 c-10.843,0.747-20.938,5.154-30.257,13.127c-9.51-5.843-19.8-9.227-30.859-10.366c0.521-3.197,1.46-6.306,2.85-9.363 c3.458-7.038,8.907-12.741,16.331-17.296c-5.609-3.384-11.227-6.799-16.854-10.279c-16.257,8.104-25.06,20.601-26.463,38.417 c-7.599,1.705-14.685,4.486-21.247,8.437c-9.164-7.677-18.996-11.917-29.496-12.632c-14.819-0.998-28.467,4.787-40.938,18.827 C6.445,96.182,0,114.867,0,136.242c-0.0 [...]
];
- const zTagSymbolMap = {};
- let zTagSymbolIndex = 0;
- function getZTagSymbolPath(zTag) {
- if (!zTagSymbolMap[zTag]) {
- zTagSymbolMap[zTag] = SYMBOL_PATHS[zTagSymbolIndex];
- zTagSymbolIndex++;
- if (zTagSymbolIndex >= SYMBOL_PATHS.length) {
- zTagSymbolIndex = 0;
- }
- }
- return zTagSymbolMap[zTag]
- }
-
-
- var COUNT = 50;
var CONTENT_COLOR = '#37A2DA';
-
var ANIMATION_DURATION_UPDATE = 1500;
+
// var rawData = initRawData(COUNT);
// console.log(JSON.stringify(rawData));
var rawData = [[1425139200000,34,0.13,2,"MD","ZD","P0"],[1425225600000,28,0.71,1.5,"MB","ZD","P1"],[1425312000000,23,0.9,2.8,"MA","ZC","P2"],[1425398400000,21,0.58,6,"MB","ZC","P3"],[1425484800000,14,0.1,1.6,"MC","ZA","P4"],[1425571200000,21,0.6,7.7,"MC","ZA","P5"],[1425657600000,23,0.31,2.6,"MC","ZC","P6"],[1425744000000,34,0.74,2.4,"MD","ZE","P7"],[1425830400000,14,0.59,2.3,"MB","ZD","P8"],[1425916800000,18,0.85,5.1,"MB","ZB","P9"],[1426003200000,36,0.96,1.2,"MC","ZC"," [...]
@@ -142,6 +118,25 @@ under the License.
var RAW_CLUSTER_CENTERS_DIMENSIONS = ['COUNT', 'CLUSTER_IDX', 'CLUSTER_CENTER_ATA', 'CLUSTER_CENTER_STE'];
+ function getFromPalette(value, palette) {
+ if (!palette.__colorMap) {
+ palette.__colorMap = {};
+ }
+ if (palette.__colorIdx == null) {
+ palette.__colorIdx = 0;
+ }
+ if (!palette.__colorMap[value]) {
+ palette.__colorMap[value] = palette[palette.__colorIdx];
+ palette.__colorIdx++;
+ if (palette.__colorIdx >= palette.length) {
+ palette.__colorIdx = 0;
+ }
+ }
+ return palette.__colorMap[value];
+ }
+
+
+
var baseOption = {
dataset: [{
id: 'raw',
@@ -224,307 +219,344 @@ under the License.
};
var BG_COLOR = '#000';
- var optionCreators = {
- 'Scatter_ATA_STE': function (datasetId) {
+
+ function makeScatterOptionCreator(renderItem) {
+ return function () {
+ var datasetId = 'rawClusters';
return {
- backgroundColor: BG_COLOR,
- grid: {
- containLabel: true
- },
- xAxis: {
- name: 'STE',
- ...AXIS_STYLE
- },
- yAxis: {
- name: 'ATA',
- ...AXIS_STYLE
- },
- dataZoom: [{
- type: 'slider',
- }, {
- type: 'inside'
- }],
- series: {
- type: 'custom',
- coordinateSystem: 'cartesian2d',
- animationDurationUpdate: ANIMATION_DURATION_UPDATE,
- datasetId: datasetId,
- encode: {
- itemName: 'ID',
- x: 'STE',
- y: 'ATA',
- tooltip: ['STE', 'ATA']
+ datasetId: datasetId,
+ option: {
+ backgroundColor: BG_COLOR,
+ grid: {
+ containLabel: true
+ },
+ xAxis: {
+ name: 'STE',
+ ...AXIS_STYLE
+ },
+ yAxis: {
+ name: 'ATA',
+ ...AXIS_STYLE
},
- renderItem: function (params, api) {
- var pos = api.coord([
- api.value('STE'),
- api.value('ATA')
- ]);
- var zTagVal = api.value('Z_TAG');
- var color = getZTagColor(zTagVal);
- // var clusterIndex = api.value('CLUSTER_IDX');
- var symbolPath = getZTagSymbolPath(zTagVal);
- return {
- type: 'circle',
- morph: true,
- shape: {
- cx: pos[0],
- cy: pos[1],
- r: 10
- },
- style: {
- fill: color,
- stroke: '#555',
- lineWidth: 0
- },
- transition: ['shape', 'style']
- };
+ dataZoom: [{
+ type: 'slider',
+ }, {
+ type: 'inside'
+ }],
+ series: {
+ type: 'custom',
+ coordinateSystem: 'cartesian2d',
+ animationDurationUpdate: ANIMATION_DURATION_UPDATE,
+ datasetId: datasetId,
+ encode: {
+ itemName: 'ID',
+ x: 'STE',
+ y: 'ATA',
+ tooltip: ['STE', 'ATA']
+ },
+ renderItem: renderItem
}
}
- };
- },
+ }
+ };
+ }
- 'Scatter_SVG_ATA_STE': function (datasetId) {
- return {
- backgroundColor: BG_COLOR,
- grid: {
- containLabel: true
- },
- xAxis: {
- name: 'STE',
- ...AXIS_STYLE
- },
- yAxis: {
- name: 'ATA',
- ...AXIS_STYLE
- },
- dataZoom: [{
- type: 'slider',
- }, {
- type: 'inside'
- }],
- series: {
- type: 'custom',
- coordinateSystem: 'cartesian2d',
- animationDurationUpdate: ANIMATION_DURATION_UPDATE,
- datasetId: datasetId,
- encode: {
- itemName: 'ID',
- x: 'STE',
- y: 'ATA',
- tooltip: ['STE', 'ATA']
+
+ var optionCreators = {
+
+ 'Scatter_circle_ATA_STE': makeScatterOptionCreator(
+ function (params, api) {
+ var pos = api.coord([
+ api.value('STE'),
+ api.value('ATA')
+ ]);
+ var zTagVal = api.value('Z_TAG');
+ var color = getFromPalette(zTagVal, Z_TAG_COLORS);
+ var symbolPath = getFromPalette(zTagVal, SYMBOL_PATHS);
+ return {
+ type: 'circle',
+ morph: true,
+ shape: {
+ cx: pos[0],
+ cy: pos[1],
+ r: 10
},
- renderItem: function (params, api) {
- var pos = api.coord([
- api.value('STE'),
- api.value('ATA')
- ]);
- var zTagVal = api.value('Z_TAG');
- var color = getZTagColor(zTagVal);
- // var clusterIndex = api.value('CLUSTER_IDX');
- var symbolPath = getZTagSymbolPath(zTagVal);
- return {
- type: 'path',
- morph: true,
- x: pos[0],
- y: pos[1],
- shape: {
- pathData: symbolPath,
- width: 40,
- height: 40
- },
- style: {
- fill: color,
- stroke: '#555',
- lineWidth: 0
- },
- transition: ['style']
- };
- }
- }
- };
- },
+ style: {
+ fill: color
+ },
+ transition: ['shape', 'style']
+ };
+ }
+ ),
+
+ 'Scatter_singleSVG_ATA_STE': makeScatterOptionCreator(
+ function (params, api) {
+ var pos = api.coord([
+ api.value('STE'),
+ api.value('ATA')
+ ]);
+ var zTagVal = api.value('Z_TAG');
+ var color = getFromPalette(zTagVal, Z_TAG_COLORS);
+ var symbolPath = getFromPalette(zTagVal, SYMBOL_PATHS);
+ return {
+ type: 'path',
+ morph: true,
+ x: pos[0],
+ y: pos[1],
+ shape: {
+ pathData: symbolPath,
+ width: 40,
+ height: 40
+ },
+ style: {
+ fill: color
+ },
+ transition: ['style']
+ };
+ }
+ ),
+
+ 'Scatter_groupShape_ATA_STE': makeScatterOptionCreator(
+ function (params, api) {
+ var pos = api.coord([
+ api.value('STE'),
+ api.value('ATA')
+ ]);
+ var zTagVal = api.value('Z_TAG');
+ var color1 = getFromPalette(zTagVal, Z_TAG_COLORS);
+ var color2 = getFromPalette(zTagVal, Z_TAG_COLORS_2);
+ return {
+ type: 'group',
+ x: pos[0],
+ y: pos[1],
+ children: [{
+ type: 'polygon',
+ morph: true,
+ shape: {
+ points: [
+ [-40, -2],
+ [40, -2],
+ [0, -35]
+ ]
+ },
+ style: {
+ fill: color1,
+ stroke: '#333',
+ lineWidth: 1
+ },
+ transition: ['shape', 'style']
+ }, {
+ type: 'rect',
+ morph: true,
+ shape: {
+ x: -20,
+ y: 0,
+ width: 40,
+ height: 30
+ },
+ style: {
+ fill: color2,
+ stroke: '#333',
+ lineWidth: 1
+ },
+ transition: ['shape', 'style']
+ }]
+ };
+ }
+ ),
- 'Bar_mSum_ATA': function (datasetId) {
+ 'Bar_mSum_ATA': function () {
+ var datasetId = 'mTagSum'
return {
- backgroundColor: BG_COLOR,
- grid: {
- containLabel: true
- },
- xAxis: {
- type: 'category',
- ...AXIS_STYLE
- },
- yAxis: {
- ...AXIS_STYLE
- },
- dataZoom: [{
- type: 'slider',
- }, {
- type: 'inside'
- }],
- series: {
- type: 'custom',
- coordinateSystem: 'cartesian2d',
- animationDurationUpdate: ANIMATION_DURATION_UPDATE,
- datasetId: datasetId,
- encode: {
- x: 'M_TAG',
- y: 'ATA',
- tooltip: ['M_TAG', 'ATA']
+ datasetId: datasetId,
+ option: {
+ backgroundColor: BG_COLOR,
+ grid: {
+ containLabel: true
},
- renderItem: function (params, api) {
- var mTagVal = api.value('M_TAG');
- var ataVal = api.value('ATA');
- var tarPos = api.coord([mTagVal, ataVal]);
- var zeroPos = api.coord([mTagVal, 0]);
- var size = api.size([mTagVal, ataVal]);
- var width = size[0] * 0.4;
- return {
- type: 'rect',
- morph: true,
- shape: {
- x: tarPos[0] - width / 2,
- y: tarPos[1],
- height: zeroPos[1] - tarPos[1],
- width: width,
- },
- style: {
- fill: CONTENT_COLOR,
- stroke: '#555',
- lineWidth: 0
- },
- transition: ['shape', 'style']
- };
+ xAxis: {
+ type: 'category',
+ ...AXIS_STYLE
+ },
+ yAxis: {
+ ...AXIS_STYLE
+ },
+ dataZoom: [{
+ type: 'slider',
+ }, {
+ type: 'inside'
+ }],
+ series: {
+ type: 'custom',
+ coordinateSystem: 'cartesian2d',
+ animationDurationUpdate: ANIMATION_DURATION_UPDATE,
+ datasetId: datasetId,
+ encode: {
+ x: 'M_TAG',
+ y: 'ATA',
+ tooltip: ['M_TAG', 'ATA']
+ },
+ renderItem: function (params, api) {
+ var mTagVal = api.value('M_TAG');
+ var ataVal = api.value('ATA');
+ var tarPos = api.coord([mTagVal, ataVal]);
+ var zeroPos = api.coord([mTagVal, 0]);
+ var size = api.size([mTagVal, ataVal]);
+ var width = size[0] * 0.4;
+ return {
+ type: 'rect',
+ morph: true,
+ shape: {
+ x: tarPos[0] - width / 2,
+ y: tarPos[1],
+ height: zeroPos[1] - tarPos[1],
+ width: width,
+ },
+ style: {
+ fill: CONTENT_COLOR,
+ stroke: '#555',
+ lineWidth: 0
+ },
+ transition: ['shape', 'style']
+ };
+ }
}
}
};
},
- 'Pie_mSum_ATA': function (datasetId) {
+ 'Pie_mSum_ATA': function () {
+ var datasetId = 'mTagSum';
return {
- backgroundColor: BG_COLOR,
- series: {
- type: 'custom',
- coordinateSystem: 'none',
- animationDurationUpdate: ANIMATION_DURATION_UPDATE,
- datasetId: datasetId,
- encode: {
- itemName: 'M_TAG',
- value: 'ATA',
- tooltip: 'ATA'
- },
- renderItem: function (params, api) {
- var context = params.context;
- if (!context.layout) {
- context.layout = true;
- var totalValue = 0;
- for (var i = 0; i < params.dataInsideLength; i++) {
- totalValue += api.value('ATA', i);
- }
- var angles = [];
- var colors = [];
- var currentAngle = -Math.PI / 2;
- for (var i = 0; i < params.dataInsideLength; i++) {
- colors.push(PIE_COLORS[i]);
- var angle = api.value('ATA', i) / totalValue * Math.PI * 2;
- angles.push([currentAngle, angle + currentAngle - 0.01]);
- currentAngle += angle;
+ datasetId: datasetId,
+ option: {
+ backgroundColor: BG_COLOR,
+ series: {
+ type: 'custom',
+ coordinateSystem: 'none',
+ animationDurationUpdate: ANIMATION_DURATION_UPDATE,
+ datasetId: datasetId,
+ encode: {
+ itemName: 'M_TAG',
+ value: 'ATA',
+ tooltip: 'ATA'
+ },
+ renderItem: function (params, api) {
+ var context = params.context;
+ if (!context.layout) {
+ context.layout = true;
+ var totalValue = 0;
+ for (var i = 0; i < params.dataInsideLength; i++) {
+ totalValue += api.value('ATA', i);
+ }
+ var angles = [];
+ var colors = [];
+ var currentAngle = -Math.PI / 2;
+ for (var i = 0; i < params.dataInsideLength; i++) {
+ colors.push(PIE_COLORS[i]);
+ var angle = api.value('ATA', i) / totalValue * Math.PI * 2;
+ angles.push([currentAngle, angle + currentAngle - 0.01]);
+ currentAngle += angle;
+ }
+ context.angles = angles;
+ context.colors = colors;
}
- context.angles = angles;
- context.colors = colors;
- }
- var width = chart.getWidth();
- var height = chart.getHeight();
- return {
- type: 'sector',
- morph: true,
- shape: {
- cx: width / 2,
- cy: height / 2,
- r: Math.min(width, height) / 3,
- r0: Math.min(width, height) / 5,
- startAngle: context.angles[params.dataIndex][0],
- endAngle: context.angles[params.dataIndex][1],
- clockwise: true
- },
- style: {
- // fill: CONTENT_COLOR,
- fill: context.colors[params.dataIndex],
- stroke: '#555',
- lineWidth: 0,
- strokeNoScale: true
- },
- transition: ['shape', 'style']
- };
+ var width = chart.getWidth();
+ var height = chart.getHeight();
+ return {
+ type: 'sector',
+ morph: true,
+ shape: {
+ cx: width / 2,
+ cy: height / 2,
+ r: Math.min(width, height) / 3,
+ r0: Math.min(width, height) / 5,
+ startAngle: context.angles[params.dataIndex][0],
+ endAngle: context.angles[params.dataIndex][1],
+ clockwise: true
+ },
+ style: {
+ // fill: CONTENT_COLOR,
+ fill: context.colors[params.dataIndex],
+ stroke: '#555',
+ lineWidth: 0,
+ strokeNoScale: true
+ },
+ transition: ['shape', 'style']
+ };
+ }
}
}
};
},
- 'Scatter_ATA_STE_Cluster_Center': function (datasetId) {
+ 'Scatter_ATA_STE_Cluster_Center': function () {
+ var datasetId = 'rawClusterCenters';
return {
- backgroundColor: BG_COLOR,
- grid: {
- containLabel: true
- },
- xAxis: {
- name: 'STE',
- ...AXIS_STYLE
- },
- yAxis: {
- name: 'ATA',
- ...AXIS_STYLE
- },
- dataZoom: [{
- type: 'slider',
- }, {
- type: 'inside'
- }],
- series: {
- type: 'custom',
- coordinateSystem: 'cartesian2d',
- animationDurationUpdate: ANIMATION_DURATION_UPDATE,
- datasetId: datasetId,
- encode: {
- x: 'CLUSTER_CENTER_STE',
- y: 'CLUSTER_CENTER_ATA',
- tooltip: ['CLUSTER_CENTER_STE', 'CLUSTER_CENTER_ATA']
+ datasetId: datasetId,
+ option: {
+ backgroundColor: BG_COLOR,
+ grid: {
+ containLabel: true
},
- renderItem: function (params, api) {
- var context = params.context;
- if (!context.layout) {
- context.layout = true;
- context.totalCount = 0;
- for (var i = 0; i < params.dataInsideLength; i++) {
- context.totalCount += api.value('COUNT', i);
+ xAxis: {
+ name: 'STE',
+ ...AXIS_STYLE
+ },
+ yAxis: {
+ name: 'ATA',
+ ...AXIS_STYLE
+ },
+ dataZoom: [{
+ type: 'slider',
+ }, {
+ type: 'inside'
+ }],
+ series: {
+ type: 'custom',
+ coordinateSystem: 'cartesian2d',
+ animationDurationUpdate: ANIMATION_DURATION_UPDATE,
+ datasetId: datasetId,
+ encode: {
+ x: 'CLUSTER_CENTER_STE',
+ y: 'CLUSTER_CENTER_ATA',
+ tooltip: ['CLUSTER_CENTER_STE', 'CLUSTER_CENTER_ATA']
+ },
+ renderItem: function (params, api) {
+ var context = params.context;
+ if (!context.layout) {
+ context.layout = true;
+ context.totalCount = 0;
+ for (var i = 0; i < params.dataInsideLength; i++) {
+ context.totalCount += api.value('COUNT', i);
+ }
}
- }
- var pos = api.coord([
- api.value('CLUSTER_CENTER_STE'),
- api.value('CLUSTER_CENTER_ATA')
- ]);
- var count = api.value('COUNT');
- var radius = count / context.totalCount * 100 + 10;
- return {
- type: 'circle',
- morph: true,
- shape: {
- cx: pos[0],
- cy: pos[1],
- r: radius,
- },
- style: {
- // fill: CONTENT_COLOR,
- fill: CLUSTER_COLORS[params.dataIndex],
- stroke: '#555',
- lineWidth: 0
- },
- transition: ['shape', 'style']
- };
+ var pos = api.coord([
+ api.value('CLUSTER_CENTER_STE'),
+ api.value('CLUSTER_CENTER_ATA')
+ ]);
+ var count = api.value('COUNT');
+ var radius = count / context.totalCount * 100 + 10;
+ return {
+ type: 'circle',
+ morph: true,
+ shape: {
+ cx: pos[0],
+ cy: pos[1],
+ r: radius,
+ },
+ style: {
+ // fill: CONTENT_COLOR,
+ fill: CLUSTER_COLORS[params.dataIndex],
+ stroke: '#555',
+ lineWidth: 0
+ },
+ transition: ['shape', 'style']
+ };
+ }
}
}
};
@@ -532,6 +564,25 @@ under the License.
};
+
+ var optionList = [];
+ var buttons = [];
+ echarts.util.each(optionCreators, function (creator, key) {
+ var optionWrap = creator();
+ optionList.push({
+ key: key,
+ dataMetaKey: optionWrap.datasetId,
+ option: optionWrap.option
+ });
+ buttons.push({
+ text: key,
+ onclick: function () {
+ player.go(key);
+ }
+ });
+ });
+
+
var player = transitionPlayer.create({
chart: function () {
return chart;
@@ -557,27 +608,7 @@ under the License.
uniqueDimension: 'CLUSTER_IDX'
}
},
- optionList: [{
- key: 'Scatter_ATA_STE',
- dataMetaKey: 'rawClusters',
- option: optionCreators['Scatter_ATA_STE']('rawClusters')
- }, {
- key: 'Scatter_SVG_ATA_STE',
- dataMetaKey: 'rawClusters',
- option: optionCreators['Scatter_SVG_ATA_STE']('rawClusters')
- }, {
- key: 'Bar_mTagSum_ATA',
- dataMetaKey: 'mTagSum',
- option: optionCreators['Bar_mSum_ATA']('mTagSum')
- }, {
- key: 'Pie_mTagSum_ATA',
- dataMetaKey: 'mTagSum',
- option: optionCreators['Pie_mSum_ATA']('mTagSum')
- }, {
- key: 'Scatter_ATA_STE_Cluster_Center',
- dataMetaKey: 'rawClusterCenters',
- option: optionCreators['Scatter_ATA_STE_Cluster_Center']('rawClusterCenters')
- }]
+ optionList: optionList
});
@@ -591,32 +622,7 @@ under the License.
option: baseOption,
lazyUpdate: true,
height: 600,
- buttons: [{
- text: 'Bar_mTagSum_ATA',
- onclick: function () {
- player.go('Bar_mTagSum_ATA');
- }
- }, {
- text: 'Scatter_ATA_STE',
- onclick: function () {
- player.go('Scatter_ATA_STE');
- }
- }, {
- text: 'Scatter_SVG_ATA_STE',
- onclick: function () {
- player.go('Scatter_SVG_ATA_STE');
- }
- }, {
- text: 'Pie_mTagSum_ATA',
- onclick: function () {
- player.go('Pie_mTagSum_ATA');
- }
- }, {
- text: 'Scatter_ATA_STE_Cluster_Center',
- onclick: function () {
- player.go('Scatter_ATA_STE_Cluster_Center');
- }
- }, {
+ buttons: buttons.concat([{
text: 'resizeToSmall',
onclick: function () {
chart.resize({
@@ -632,10 +638,10 @@ under the License.
height: 600
});
}
- }]
+ }])
});
- player.go('Scatter_SVG_ATA_STE');
+ player.go('Scatter_singleSVG_ATA_STE');
});
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org