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/06/27 04:38:45 UTC
[incubator-echarts] 03/03: fix: fix during call and enhance test
cases.
This is an automated email from the ASF dual-hosted git repository.
sushuang pushed a commit to branch custom-series-enhance
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git
commit 5a6ccc80c07811a78f31fc4cdaa936f69510db82
Author: 100pah <su...@gmail.com>
AuthorDate: Fri Jun 26 15:35:31 2020 +0800
fix: fix during call and enhance test cases.
---
src/chart/custom.ts | 106 ++++---
src/export.ts | 1 +
src/util/graphic.ts | 3 +
test/custom-transition2.html | 678 +++++++++++++++++++++++++++++++++----------
4 files changed, 603 insertions(+), 185 deletions(-)
diff --git a/src/chart/custom.ts b/src/chart/custom.ts
index 05a9fd7..1a06308 100644
--- a/src/chart/custom.ts
+++ b/src/chart/custom.ts
@@ -20,7 +20,7 @@
import {__DEV__} from '../config';
import {
- hasOwn, assert, isString, retrieve2, retrieve3, defaults, each, keys, isArrayLike
+ hasOwn, assert, isString, retrieve2, retrieve3, defaults, each, keys, isArrayLike, bind
} from 'zrender/src/core/util';
import * as graphicUtil from '../util/graphic';
import {getDefaultLabel} from './helper/labelHelper';
@@ -87,9 +87,8 @@ const inner = makeInner<{
customImagePath: CustomImageOption['style']['image'];
// customText: string;
txConZ2Set: number;
- orginalDuring: Element['updateDuringAnimation'];
- customDuring: CustomZRPathOption['during'];
- leaveToProps: ElementProps
+ leaveToProps: ElementProps;
+ userDuring: CustomBaseElementOption['during'];
}, Element>();
type CustomExtraElementInfo = Dictionary<unknown>;
@@ -263,7 +262,6 @@ interface CustomSeriesOption extends
interface LooseElementProps extends ElementProps {
style?: ZRStyleProps;
shape?: Dictionary<unknown>;
- extra?: Dictionary<unknown>;
}
// Also compat with ec4, where
@@ -576,16 +574,17 @@ function createEl(elOption: CustomElementOption): Element {
* ----------------------------------------------------------
* [STRATEGY_MERGE] Merge properties or erase all properties:
*
- * Based on the fact that the existing zr element probably be reused, we discuss whether
+ * Based on the fact that the existing zr element probably be reused, we now consider whether
* merge or erase all properties to the exsiting elements.
- * + "Merge" means that if a certain props is not specified, do not assign to the existing element.
- * + "Erase all" means that assign all of the available props whatever it specified by users.
+ * That is, if a certain props is not specified in the lastest return of `renderItem`:
+ * + "Merge" means that do not modify the value on the existing element.
+ * + "Erase all" means that use a default value to the existing element.
*
* "Merge" might bring some unexpected state retaining for users and "erase all" seams to be
- * more safe. But "erase all" force users to specify all of the props each time, which
- * theoretically disables the chance of performance optimization (e.g., just generete shape
- * and style at the first time rather than always do that). And "force user set all of the props"
- * might bring trouble to specify which props need to perform "transition animation".
+ * more safe. "erase all" force users to specify all of the props each time, which is recommanded
+ * in most cases.
+ * But "erase all" theoretically disables the chance of performance optimization (e.g., just
+ * generete shape and style at the first time rather than always do that).
* So we still use "merge" rather than "erase all". If users need "erase all", they can
* simple always set all of the props each time.
* Some "object-like" config like `textConfig`, `textContent`, `style` which are not needed for
@@ -690,29 +689,30 @@ function updateElNormal(
hasOwn(elOption, 'invisible') && (elDisplayable.invisible = elOption.invisible);
}
+ // Do not use `el.updateDuringAnimation` here becuase `el.updateDuringAnimation` will
+ // be called mutiple time in each animation frame. For example, if both "transform" props
+ // and shape props and style props changed, it will generate three animator and called
+ // one-by-one in each animation frame.
+ // We use the during in `animateTo/From` params.
+ const userDuring = elOption.during;
+ // For simplicity, if during not specified, the previous during will not work any more.
+ inner(el).userDuring = userDuring;
+ const cfgDuringCall = userDuring ? bind(duringCall, { el: el, userDuring: userDuring }) : null;
+
el.attr(allProps);
- const params = {dataIndex: dataIndex, isFrom: true};
+ const cfg = {
+ dataIndex: dataIndex,
+ isFrom: true,
+ during: cfgDuringCall
+ };
isInit
- ? graphicUtil.initProps(el, transFromProps, seriesModel, params)
- : graphicUtil.updateProps(el, transFromProps, seriesModel, params);
+ ? graphicUtil.initProps(el, transFromProps, seriesModel, cfg)
+ : graphicUtil.updateProps(el, transFromProps, seriesModel, cfg);
// Merge by default.
hasOwn(elOption, 'silent') && (el.silent = elOption.silent);
hasOwn(elOption, 'ignore') && (el.ignore = elOption.ignore);
- const customDuringMounted = el.updateDuringAnimation === elUpdateDuringAnimation;
- if (elOption.during) {
- const innerEl = inner(el);
- if (!customDuringMounted) {
- innerEl.orginalDuring = el.updateDuringAnimation;
- el.updateDuringAnimation = elUpdateDuringAnimation;
- }
- innerEl.customDuring = elOption.during;
- }
- else if (customDuringMounted) {
- el.updateDuringAnimation = inner(el).orginalDuring;
- }
-
if (!isTextContent) {
// `elOption.info` enables user to mount some info on
// elements and use them in event handlers.
@@ -723,6 +723,7 @@ function updateElNormal(
styleOpt ? el.dirty() : el.markRedraw();
}
+
// See [STRATEGY_TRANSITION]
function prepareShapeOrExtraUpdate(
mainAttr: 'shape' | 'extra',
@@ -1023,30 +1024,54 @@ function assertNotReserved(key: string) {
}
}
-function elUpdateDuringAnimation(this: Element, key: string): void {
+function duringCall(
+ this: {
+ el: Element;
+ userDuring: CustomBaseElementOption['during']
+ }
+): void {
// Do not provide "percent" until some requirements come.
// Because consider thies case:
// enterFrom: {x: 100, y: 30}, transition: 'x'.
// And enter duration is different from update duration.
// Thus it might be confused about the meaning of "percent" in during callback.
- const innerEl = inner(this);
- // FIXME `this.markRedraw();` directly ?
- innerEl.orginalDuring.call(this, key);
- const customDuring = innerEl.customDuring;
+ const scope = this;
+ const el = scope.el;
+ if (!el) {
+ return;
+ }
+ // If el is remove from zr by reason like legend, during still need to called,
+ // becuase el will be added back to zr and the prop value should not be incorrect.
+
+ const newstUserDuring = inner(el).userDuring;
+ const scopeUserDuring = scope.userDuring;
+ // Ensured a during is only called once in each animation frame.
+ // If a during is called multiple times in one frame, maybe some users' calulation logic
+ // might be wrong (not sure whether this usage exists).
+ // The case of a during might be called twice can be: by default there is a animator for
+ // 'x', 'y' when init. Before the init animation finished, call `setOption` to start
+ // another animators for 'style'/'shape'/'extra'.
+ if (newstUserDuring !== scopeUserDuring) {
+ // release
+ scope.el = scope.userDuring = null;
+ return;
+ }
- tmpDuringScope.el = this;
+ tmpDuringScope.el = el;
tmpDuringScope.isShapeDirty = false;
tmpDuringScope.isStyleDirty = false;
- customDuring(customDuringAPI);
+ // Give no `this` to user in "during" calling.
+ scopeUserDuring(customDuringAPI);
- if (tmpDuringScope.isShapeDirty && (this as graphicUtil.Path).dirtyShape) {
- (this as graphicUtil.Path).dirtyShape();
+ if (tmpDuringScope.isShapeDirty && (el as graphicUtil.Path).dirtyShape) {
+ (el as graphicUtil.Path).dirtyShape();
}
- if (tmpDuringScope.isStyleDirty && (this as Displayable).dirtyStyle) {
- (this as Displayable).dirtyStyle();
+ if (tmpDuringScope.isStyleDirty && (el as Displayable).dirtyStyle) {
+ (el as Displayable).dirtyStyle();
}
// markRedraw() will be called by default in during.
+ // FIXME `this.markRedraw();` directly ?
// FIXME: if in future meet the case that some prop will be both modified in `during` and `state`,
// consider the issue that the prop might be incorrect when return to "normal" state.
@@ -1845,6 +1870,9 @@ function mergeChildren(
);
}
for (let i = el.childCount() - 1; i >= index; i--) {
+ // Do not supprot leave elements that are not mentioned in the latest
+ // `renderItem` return. Otherwise users may not have a clear and simple
+ // concept that how to contorl all of the elements.
doRemoveEl(el.childAt(i), seriesModel, el);
}
}
diff --git a/src/export.ts b/src/export.ts
index 7f3e459..f45beb3 100644
--- a/src/export.ts
+++ b/src/export.ts
@@ -74,6 +74,7 @@ const GRAPHIC_KEYS = [
'Image',
'Text',
'Circle',
+ 'Ellipse',
'Sector',
'Ring',
'Polygon',
diff --git a/src/util/graphic.ts b/src/util/graphic.ts
index fe27f60..9806368 100644
--- a/src/util/graphic.ts
+++ b/src/util/graphic.ts
@@ -27,6 +27,7 @@ import ZRImage, { ImageStyleProps } from 'zrender/src/graphic/Image';
import Group from 'zrender/src/graphic/Group';
import ZRText, { TextStyleProps } from 'zrender/src/graphic/Text';
import Circle from 'zrender/src/graphic/shape/Circle';
+import Ellipse from 'zrender/src/graphic/shape/Ellipse';
import Sector from 'zrender/src/graphic/shape/Sector';
import Ring from 'zrender/src/graphic/shape/Ring';
import Polygon from 'zrender/src/graphic/shape/Polygon';
@@ -1565,6 +1566,7 @@ export const getECData = makeInner<ECData, Element>();
// Register built-in shapes. These shapes might be overwirtten
// by users, although we do not recommend that.
registerShape('circle', Circle);
+registerShape('ellipse', Ellipse);
registerShape('sector', Sector);
registerShape('ring', Ring);
registerShape('polygon', Polygon);
@@ -1579,6 +1581,7 @@ export {
ZRImage as Image,
ZRText as Text,
Circle,
+ Ellipse,
Sector,
Ring,
Polygon,
diff --git a/test/custom-transition2.html b/test/custom-transition2.html
index 3e9eb56..f9a8338 100644
--- a/test/custom-transition2.html
+++ b/test/custom-transition2.html
@@ -38,6 +38,11 @@ under the License.
<div id="main-cluster-step"></div>
+ <div id="during-case-continue"></div>
+ <div id="during-case-get-curr"></div>
+ <div id="during-case-partial-change"></div>
+ <div id="during-first-frame-correct"></div>
+ <div id="during-ensure-once-in-each-frame"></div>
@@ -113,10 +118,12 @@ under the License.
centroids: stepData.centroids,
points: [],
newestClusterIndex: newestClusterIdx,
- newestClusterMaxAssessmentIndex: -1
+ newestClusterMaxDistance: -Infinity
};
result.push(stepResult);
+ var newestCenter = stepData.centroids[newestClusterIdx];
+ var newestClusterPoints = ptsInCluster[newestClusterIdx];
for (var cIdx = 0; cIdx < ptsInCluster.length; cIdx++) {
var clusterPoints = ptsInCluster[cIdx];
@@ -128,15 +135,15 @@ under the License.
throw Error(key);
}
stepResult.points[ptsIdx] = [point[0], point[1], cIdx, newestClusterIdx];
- }
- }
- var assessment = -Infinity;
- for (var i = 0; i < stepData.clusterAssment.length; i++) {
- var assessmentRecord = stepData.clusterAssment[i];
- if (assessmentRecord[0] === newestClusterIdx && assessmentRecord[1] > assessment) {
- assessment = assessmentRecord[1];
- stepResult.newestClusterMaxAssessmentIndex = i;
+ if (cIdx === newestClusterIdx) {
+ var dx = newestCenter[0] - point[0];
+ var dy = newestCenter[1] - point[1];
+ var dist = Math.pow(dx * dx + dy * dy, 0.5);
+ if (dist > stepResult.newestClusterMaxDistance) {
+ stepResult.newestClusterMaxDistance = dist;
+ }
+ }
}
}
}
@@ -154,16 +161,11 @@ under the License.
var contentColor = colorAll[clusterIdx];
addColorTransition(extra, contentColor, 'content');
- // var borderColor = isNewCluster ? '#333' : '#fff';
- // addColorTransition(shape, borderColor, 'border');
return {
type: 'circle',
x: coord[0],
y: coord[1],
- // scaleX: isNewCluster ? 1.2 : 1,
- // scaleY: isNewCluster ? 1.2 : 1,
- // transition: ['scaleX', 'scaleY'],
shape: {
cx: 0,
cy: 0,
@@ -181,8 +183,6 @@ under the License.
during: function (apiDuring) {
var currContentColor = getColorInTransition(apiDuring, 'content');
apiDuring.setStyle('fill', currContentColor);
- // var currBorderColor = getColorInTransition(apiDuring, 'border');
- // apiDuring.setStyle('stroke', currBorderColor);
}
};
}
@@ -208,153 +208,43 @@ under the License.
}
function renderBoundary(params, api) {
- var center = api.coord([api.value(2), api.value(3)]);
- var endP = api.coord([api.value(0), api.value(1)]);
- var diffX = center[0] - endP[0];
- var diffY = center[1] - endP[1];
- var radius = Math.pow(diffX * diffX + diffY * diffY, 0.5) + 10;
- var newCluIdx = api.value(4);
+ var xVal = api.value(0);
+ var yVal = api.value(1);
+ var maxDist = api.value(2);
+ var center = api.coord([xVal, yVal]);
+ var size = api.size([maxDist, maxDist]);
+ var renderNumberStar;
return {
- type: 'circle',
+ type: 'ellipse',
shape: {
- cx: 0,
- cy: 0,
- r: 0
+ cx: isNaN(center[0]) ? 0 : center[0],
+ cy: isNaN(center[1]) ? 0 : center[1],
+ rx: isNaN(size[0]) ? 0 : size[0] + 15,
+ ry: isNaN(size[1]) ? 0 : size[1] + 15
},
extra: {
- cxNext: isNaN(center[0]) ? 0 : center[0],
- cyNext: isNaN(center[1]) ? 0 : center[1],
- rNext: isNaN(radius) ? 0 : radius,
renderNumber: ++renderNumber,
transition: 'renderNumber'
},
style: {
fill: null,
- stroke: '#bbb',
- lineDash: [5, 5],
- lineWidth: 3,
- // opacity: 0
+ stroke: 'rgba(0,0,0,0.2)',
+ lineDash: [4, 4],
+ lineWidth: 4
},
during: function (apiDuring) {
var currNum = apiDuring.getExtra('renderNumber');
- if (apiDuring.getStyle('opacity') < 0.9) {
- apiDuring
- .setShape('cx', apiDuring.getExtra('cxNext'))
- .setShape('cy', apiDuring.getExtra('cyNext'))
- .setShape('r', apiDuring.getExtra('rNext'));
- }
- apiDuring.setStyle('opacity', 1 - (renderNumber - currNum));
+ !renderNumberStar && (renderNumberStar = currNum);
+ apiDuring.setStyle('opacity', (currNum - renderNumberStar) / (renderNumber - renderNumberStar));
}
};
-
- // return {
- // type: 'group',
- // children: [{
- // type: 'circle',
- // shape: {
- // cx: 0,
- // cy: 0,
- // r: 0,
- // },
- // extra: {
- // renderNumber: ++renderNumber,
- // cxNext: isNaN(center[0]) ? 0 : center[0],
- // cyNext: isNaN(center[1]) ? 0 : center[1],
- // rNext: isNaN(radius) ? 0 : radius,
- // transition: 'renderNumber'
- // },
- // style: {
- // fill: null,
- // stroke: '#bbb',
- // lineDash: [5, 5],
- // lineWidth: 3
- // },
- // during: function (apiDuring) {
- // var currNum = apiDuring.getExtra('renderNumber');
- // var progress = 0.5 - (renderNumber - currNum);
- // if (progress >= 0) {
- // apiDuring
- // .setShape('cx', apiDuring.getExtra('cxNext'))
- // .setShape('cy', apiDuring.getExtra('cyNext'))
- // .setShape('r', apiDuring.getExtra('rNext'));
- // }
- // var opacity = Math.abs(progress) / 0.5;
- // apiDuring.setStyle('opacity', opacity);
- // }
- // }, {
- // type: 'circle',
- // shape: {
- // cx: 0,
- // cy: 0,
- // r: 0,
- // },
- // extra: {
- // renderNumber: ++renderNumber,
- // cxNext: isNaN(center[0]) ? 0 : center[0],
- // cyNext: isNaN(center[1]) ? 0 : center[1],
- // rNext: isNaN(radius) ? 0 : radius,
- // transition: 'renderNumber'
- // },
- // style: {
- // fill: null,
- // stroke: '#bbb',
- // lineDash: [5, 5],
- // lineWidth: 3
- // },
- // during: function (apiDuring) {
- // var currNum = apiDuring.getExtra('renderNumber');
- // var progress = 0.5 - (renderNumber - currNum);
- // if (progress >= 0) {
- // apiDuring
- // .setShape('cx', apiDuring.getExtra('cxNext'))
- // .setShape('cy', apiDuring.getExtra('cyNext'))
- // .setShape('r', apiDuring.getExtra('rNext'));
- // }
- // var opacity = Math.abs(progress) / 0.5;
- // apiDuring.setStyle('opacity', opacity);
- // }
- // }]
- // };
- // return {
- // type: 'circle',
- // shape: {
- // cx: 0,
- // cy: 0,
- // r: 0,
- // },
- // extra: {
- // renderNumber: ++renderNumber,
- // cxNext: isNaN(center[0]) ? 0 : center[0],
- // cyNext: isNaN(center[1]) ? 0 : center[1],
- // rNext: isNaN(radius) ? 0 : radius,
- // transition: 'renderNumber'
- // },
- // style: {
- // fill: null,
- // stroke: '#bbb',
- // lineDash: [5, 5],
- // lineWidth: 3
- // },
- // during: function (apiDuring) {
- // var currNum = apiDuring.getExtra('renderNumber');
- // var progress = 0.5 - (renderNumber - currNum);
- // if (progress >= 0) {
- // apiDuring
- // .setShape('cx', apiDuring.getExtra('cxNext'))
- // .setShape('cy', apiDuring.getExtra('cyNext'))
- // .setShape('r', apiDuring.getExtra('rNext'));
- // }
- // var opacity = Math.abs(progress) / 0.5;
- // apiDuring.setStyle('opacity', opacity);
- // }
- // };
}
function makeStepOption(option, stepResult) {
var centroids = stepResult.centroids || [];
var points = stepResult.points;
- var newMaxAssmIdx = stepResult.newestClusterMaxAssessmentIndex;
+ var maxDist = stepResult.newestClusterMaxDistance;
var newCluIdx = stepResult.newestClusterIndex;
option.options.push({
@@ -369,13 +259,12 @@ under the License.
type: 'custom',
renderItem: renderBoundary,
animationDuration: 3000,
+ silent: true,
data: [
[
- (points[newMaxAssmIdx] || [])[0],
- (points[newMaxAssmIdx] || [])[1],
(centroids[newCluIdx] || [])[0],
(centroids[newCluIdx] || [])[1],
- newCluIdx
+ maxDist
]
]
}]
@@ -442,6 +331,503 @@ under the License.
</script>
+
+
+
+
+
+
+
+
+
+ <script>
+ require(['echarts'], function (echarts) {
+
+ function renderItem(params, api) {
+ return {
+ type: 'circle',
+ shape: {
+ cx: api.value(0),
+ cy: api.value(1),
+ r: 40,
+ transition: 'cx'
+ },
+ style: {
+ fill: 'green',
+ },
+ };
+ }
+
+ var baseX = 60;
+ var baseY = 80
+
+ var option = {
+ series: [{
+ id: 'a',
+ type: 'custom',
+ coordinateSystem: 'none',
+ renderItem: renderItem,
+ animationDuration: 3000,
+ animationDurationUpdate: 6000,
+ data: [[baseX, baseY]]
+ }]
+ };
+
+
+ var chart = testHelper.create(echarts, 'during-case-continue', {
+ title: [
+ 'Click "move" several times **before animation finished**',
+ 'The cirle should keep move to right **without jump**'
+ ],
+ height: 200,
+ option: option,
+ buttons: [{
+ text: 'move',
+ onclick: function () {
+ chart.setOption({
+ series: {
+ id: 'a',
+ data: [[baseX += 60, baseY]]
+ }
+ });
+ }
+ }]
+ });
+ });
+ </script>
+
+
+
+
+
+
+
+
+
+
+
+
+ <script>
+ require(['echarts'], function (echarts) {
+
+ function renderItem(params, api) {
+ var renderNumberStar;
+ return {
+ type: 'circle',
+ shape: {
+ cx: api.value(0),
+ cy: api.value(1),
+ r: 40
+ },
+ extra: {
+ renderNumber: ++renderNumber,
+ transition: 'renderNumber'
+ },
+ style: {
+ fill: 'green'
+ },
+ during: function (apiDuring) {
+ var currNum = apiDuring.getExtra('renderNumber');
+ !renderNumberStar && (renderNumberStar = currNum);
+ apiDuring.setStyle('opacity', (currNum - renderNumberStar) / (renderNumber - renderNumberStar));
+ }
+ };
+ }
+
+ var renderNumber = 1;
+ var baseX = 60;
+ var baseY = 100;
+
+ var option = {
+ series: [{
+ id: 'a',
+ type: 'custom',
+ coordinateSystem: 'none',
+ renderItem: renderItem,
+ animationDuration: 3000,
+ animationDurationUpdate: 10000,
+ data: [[baseX, baseY]]
+ }]
+ };
+
+ var chart = testHelper.create(echarts, 'during-case-get-curr', {
+ title: [
+ 'Click next several times **before animation finished**',
+ 'Each click, the circle **disappear immediately** and **fade in** at right a bit',
+ 'MUST **not blink**'
+ ],
+ height: 200,
+ option: option,
+ buttons: [{
+ text: 'next',
+ onclick: function () {
+ chart.setOption({
+ series: {
+ id: 'a',
+ data: [[baseX += 20, baseY]]
+ }
+ });
+ }
+ }]
+ });
+ });
+ </script>
+
+
+
+
+
+
+
+
+
+
+
+
+ <script>
+ require(['echarts'], function (echarts) {
+
+ var baseX = 100;
+ var baseY = 100;
+
+ function renderItem(params, api) {
+ var textOpt = {
+ type: 'text',
+ extra: { },
+ transition: [], // disable the default transition of x y.
+ style: { x: 20, y: 20, fontSize: 20, stroke: 'green' },
+ during: function (apiDuring) {
+ var x = apiDuring.getExtra('x');
+ var y = apiDuring.getExtra('y');
+ apiDuring.setStyle('text', makeText(x, y));
+ }
+ };
+ var movingCircleOpt = {
+ type: 'circle',
+ shape: { cx: 0, cy: 0, r: 10 },
+ extra: { },
+ style: { fill: 'red' },
+ transition: [], // disable the default transition of x y.
+ during: function (apiDuring) {
+ var x = apiDuring.getExtra('x');
+ var y = apiDuring.getExtra('y');
+ apiDuring.setTransform('x', x).setTransform('y', y);
+ }
+ };
+
+ var cmd = api.value(0);
+ if (cmd === 'init') {
+ textOpt.extra.x = baseX;
+ textOpt.extra.y = baseY;
+ textOpt.extra.transition = ['x', 'y'];
+ textOpt.style.text = makeText(baseX, baseY);
+ movingCircleOpt.x = baseX;
+ movingCircleOpt.y = baseY;
+ movingCircleOpt.extra.x = baseX;
+ movingCircleOpt.extra.y = baseY;
+ movingCircleOpt.extra.transition = ['x', 'y'];
+ }
+ else if (cmd === 'x') {
+ baseX += 100;
+ textOpt.extra.x = baseX;
+ textOpt.extra.transition = ['x'];
+ // textOpt.style.text = makeText(baseX, baseY);
+ movingCircleOpt.extra.x = baseX;
+ movingCircleOpt.extra.transition = ['x'];
+ }
+ else if (cmd === 'y') {
+ baseY += 100;
+ textOpt.extra.y = baseY;
+ textOpt.extra.transition = ['y'];
+ // textOpt.style.text = makeText(baseX, baseY);
+ movingCircleOpt.extra.y = baseY;
+ movingCircleOpt.extra.transition = ['y'];
+ }
+
+ return {
+ type: 'group',
+ children: [
+ textOpt,
+ movingCircleOpt,
+ {
+ // Standard circle: used to check the result of moving circle.
+ type: 'circle',
+ x: baseX,
+ y: baseY,
+ transition: [], // disable the default transition of x y.
+ shape: {cx: 0, cy: 0, r: 15},
+ style: {fill: '#aaa'},
+ z2: -1
+ }
+ ]
+ };
+ }
+
+ function makeText(x, y) {
+ return ['x: ' + x.toFixed(2), 'y: ' + y.toFixed(2)].join('\n');
+ }
+
+ var option = {
+ series: [{
+ id: 'a',
+ type: 'custom',
+ coordinateSystem: 'none',
+ renderItem: renderItem,
+ animationDuration: 3000,
+ animationDurationUpdate: 5000,
+ data: [['init']]
+ }]
+ };
+
+
+ var chart = testHelper.create(echarts, 'during-case-partial-change', {
+ title: [
+ 'Partial change props test:',
+ 'Click "add x" and "add y" before animation finished.',
+ 'The red circle animation should be smooth **without jump**.',
+ 'The red circle should be finally **reach at the grey circle**.',
+ 'The **text should be correct**.',
+ ],
+ height: 500,
+ option: option,
+ buttons: [{
+ text: 'add x 100',
+ onclick: function () {
+ chart.setOption({
+ series: {
+ id: 'a',
+ data: [['x']]
+ }
+ });
+ }
+ }, {
+ text: 'add y 100',
+ onclick: function () {
+ chart.setOption({
+ series: {
+ id: 'a',
+ data: [['y']]
+ }
+ });
+ }
+ }]
+ });
+ });
+ </script>
+
+
+
+
+
+
+ <script>
+ require(['echarts'], function (echarts) {
+ var resultPrinted = false;
+
+ function renderItem(params, api) {
+ return {
+ type: 'text',
+ extra: {
+ renderNumber: renderNumber,
+ transition: 'renderNumber'
+ },
+ style: {
+ x: 100,
+ y: 50,
+ fontSize: 30,
+ enterFrom: {
+ x: 10
+ }
+ },
+ during: function (apiDuring) {
+ var currNum = apiDuring.getExtra('renderNumber');
+ if (resultPrinted || currNum <= 2) {
+ return;
+ }
+ resultPrinted = true;
+ if (currNum === 3) {
+ apiDuring.setStyle('text', 'TEST FAIL');
+ apiDuring.setStyle('fill', 'red');
+ }
+ else {
+ apiDuring.setStyle('text', 'TEST PASS');
+ apiDuring.setStyle('fill', 'green');
+ }
+ }
+ };
+ }
+
+ var renderNumber = 2;
+
+ var option = {
+ series: [{
+ id: 'a',
+ type: 'custom',
+ coordinateSystem: 'none',
+ renderItem: renderItem,
+ animationDuration: 10000,
+ animationDurationUpdate: 10000,
+ data: [[10]]
+ }]
+ };
+
+ var chart = testHelper.create(echarts, 'during-first-frame-correct', {
+ title: [
+ 'Test the first during call should not get the target value:',
+ 'Should print TEST PASS'
+ ],
+ option: option,
+ height: 200
+ });
+
+ chart && setTimeout(function () {
+ renderNumber = 3;
+ chart.setOption({
+ series: {
+ id: 'a',
+ data: [[10]]
+ }
+ });
+ // Set option before init finished.
+ }, 100);
+ });
+ </script>
+
+
+
+
+
+
+
+
+ <script>
+ require(['echarts'], function (echarts) {
+ var resultPrinted = false;
+ var currX = 0;
+ var currFontSize = 16;
+
+ function renderItem(params, api) {
+ var cmd = api.value(0);
+
+ var opt = {
+ type: 'text',
+ extra: {
+ renderNumber: renderNumber,
+ transition: 'renderNumber'
+ },
+ style: {
+ x: 100,
+ y: 50,
+ fontSize: currFontSize,
+ fill: 'green'
+ }
+ };
+
+ if (cmd !== 'noDuring') {
+ opt.during = function (apiDuring) {
+ duringCount++;
+ var currNum = apiDuring.getExtra('renderNumber');
+ apiDuring.setStyle(
+ 'text',
+ 'during count: ' + duringCount + '\n' + 'rAF count: ' + rAFCount
+ );
+ };
+ }
+ if (cmd === 'addX') {
+ currX += 50;
+ opt.x = currX;
+ opt.transition = 'x';
+ }
+ if (cmd === 'addFontSize') {
+ currFontSize += 8;
+ opt.style.fontSize = currFontSize;
+ opt.style.transition = 'fontSize';
+ }
+
+ return opt;
+ }
+
+ var renderNumber = 2;
+
+ var option = {
+ series: [{
+ id: 'a',
+ type: 'custom',
+ coordinateSystem: 'none',
+ renderItem: renderItem,
+ animationDuration: 3000,
+ animationDurationUpdate: 3000,
+ data: [[10]]
+ }]
+ };
+
+ var chart = testHelper.create(echarts, 'during-ensure-once-in-each-frame', {
+ title: [
+ 'Test during only called once in each:',
+ 'In **init** and after **click the buttons**,',
+ 'during count and rAF count',
+ 'should **be the same** (may be 1 different)'
+ ],
+ option: option,
+ height: 200,
+ button: [{
+ text: 'add x 50',
+ onclick: function () {
+ chart.setOption({ series: { id: 'a', data: [['addX']] } });
+ startCountFrame();
+ }
+ }, {
+ text: 'add fontSize 8',
+ onclick: function () {
+ chart.setOption({ series: { id: 'a', data: [['addFontSize']] } });
+ startCountFrame();
+ }
+ }]
+ });
+
+ var rAFCount = 0;
+ var duringCount = 0;
+ var rAFId;
+
+ function startCountFrame() {
+ stopCountFrame();
+ rAFId = requestAnimationFrame(countFrame);
+
+ function countFrame() {
+ rAFCount++;
+ rAFId = requestAnimationFrame(countFrame);
+ }
+ }
+
+ function stopCountFrame() {
+ if (rAFId != null) {
+ cancelAnimationFrame(rAFId);
+ rAFId = null;
+ }
+ }
+
+ if (chart) {
+ chart.on('finished', function () {
+ stopCountFrame();
+ });
+
+ startCountFrame();
+
+ setTimeout(function () {
+ renderNumber = 3;
+ chart.setOption({ series: { id: 'a', data: [['init']] } });
+ // Set option before init finished.
+ startCountFrame();
+ }, 100);
+ }
+ });
+ </script>
+
+
+
+
+
+
</body>
</html>
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org