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