You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by sh...@apache.org on 2020/03/28 13:35:31 UTC

[incubator-echarts] branch next updated: refact: use x, y, scaleX, scaleY instead of position and scale in Element

This is an automated email from the ASF dual-hosted git repository.

shenyi 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 444f6c7  refact: use x, y, scaleX, scaleY instead of position and scale in Element
444f6c7 is described below

commit 444f6c79cd9aef1cab692722f2054a76eedfda41
Author: pissang <bm...@gmail.com>
AuthorDate: Sat Mar 28 21:34:23 2020 +0800

    refact: use x, y, scaleX, scaleY instead of position and scale in Element
---
 src/action/roamHelper.ts                          | 13 +++--
 src/chart/bar/PictorialBarView.ts                 | 59 +++++++++++++-------
 src/chart/funnel/FunnelView.ts                    |  3 +-
 src/chart/graph/GraphView.ts                      |  9 ++--
 src/chart/graph/graphHelper.ts                    |  3 +-
 src/chart/helper/EffectLine.ts                    | 18 +++----
 src/chart/helper/EffectPolyline.ts                | 15 +++---
 src/chart/helper/EffectSymbol.ts                  | 10 ++--
 src/chart/helper/Line.ts                          | 66 ++++++++++++-----------
 src/chart/helper/Symbol.ts                        | 61 ++++++++++++---------
 src/chart/helper/SymbolDraw.ts                    | 12 +++--
 src/chart/line/LineView.ts                        | 13 ++++-
 src/chart/pie/PieView.ts                          | 14 ++---
 src/chart/radar/RadarView.ts                      | 10 ++--
 src/chart/sankey/SankeyView.ts                    |  3 +-
 src/chart/sunburst/SunburstPiece.ts               |  5 +-
 src/chart/themeRiver/ThemeRiverView.ts            |  3 +-
 src/chart/tree/TreeView.ts                        | 47 ++++++++--------
 src/chart/treemap/TreemapView.ts                  | 25 +++++----
 src/component/axis/AxisBuilder.ts                 | 20 +++----
 src/component/axis/ParallelAxisView.ts            |  3 +-
 src/component/axisPointer/BaseAxisPointer.ts      | 13 +++--
 src/component/axisPointer/CartesianAxisPointer.ts | 13 ++---
 src/component/axisPointer/SingleAxisPointer.ts    | 13 +++--
 src/component/axisPointer/viewHelper.ts           |  3 +-
 src/component/calendar/CalendarView.ts            | 14 ++---
 src/component/dataZoom/SliderZoomView.ts          | 17 +++---
 src/component/helper/BrushController.ts           | 12 +++--
 src/component/helper/MapDraw.ts                   | 23 ++++----
 src/component/legend/LegendView.ts                | 15 ++++--
 src/component/legend/ScrollableLegendView.ts      | 18 ++++---
 src/component/timeline/SliderTimelineView.ts      | 49 ++++++++---------
 src/component/title.ts                            |  3 +-
 src/component/toolbox/ToolboxView.ts              |  4 +-
 src/component/tooltip/TooltipRichContent.ts       |  7 ++-
 src/component/tooltip/TooltipView.ts              |  3 +-
 src/component/visualMap/ContinuousModel.ts        |  3 +-
 src/component/visualMap/ContinuousView.ts         | 16 +++---
 src/coord/View.ts                                 | 23 ++++----
 src/coord/geo/Geo.ts                              |  3 +-
 src/coord/geo/geoSVGLoader.ts                     |  5 +-
 src/util/graphic.ts                               | 15 ++++--
 src/util/layout.ts                                | 16 ++++--
 test/pie.html                                     |  5 +-
 44 files changed, 402 insertions(+), 303 deletions(-)

diff --git a/src/action/roamHelper.ts b/src/action/roamHelper.ts
index b45568f..f1b65fa 100644
--- a/src/action/roamHelper.ts
+++ b/src/action/roamHelper.ts
@@ -59,14 +59,13 @@ export function updateCenterAndZoom(
         }
 
         // Zoom on given point(originX, originY)
-        view.scale[0] *= zoom;
-        view.scale[1] *= zoom;
-        const position = view.position;
-        const fixX = (payload.originX - position[0]) * (zoom - 1);
-        const fixY = (payload.originY - position[1]) * (zoom - 1);
+        view.scaleX *= zoom;
+        view.scaleY *= zoom;
+        const fixX = (payload.originX - view.x) * (zoom - 1);
+        const fixY = (payload.originY - view.y) * (zoom - 1);
 
-        position[0] -= fixX;
-        position[1] -= fixY;
+        view.x -= fixX;
+        view.y -= fixY;
 
         view.updateTransform();
         // Get the new center
diff --git a/src/chart/bar/PictorialBarView.ts b/src/chart/bar/PictorialBarView.ts
index 99479d3..e52ffbc 100644
--- a/src/chart/bar/PictorialBarView.ts
+++ b/src/chart/bar/PictorialBarView.ts
@@ -33,6 +33,7 @@ import type Displayable from 'zrender/src/graphic/Displayable';
 import type Axis2D from '../../coord/cartesian/Axis2D';
 import type Element from 'zrender/src/Element';
 import { getDefaultLabel } from '../helper/labelHelper';
+import { PathProps } from 'zrender/src/graphic/Path';
 
 
 const BAR_BORDER_WIDTH_QUERY = ['itemStyle', 'borderWidth'] as const;
@@ -403,7 +404,8 @@ function prepareLineWidth(
 
     if (valueLineWidth) {
         pathForLineWidth.attr({
-            scale: symbolScale.slice(),
+            scaleX: symbolScale[0],
+            scaleY: symbolScale[1],
             rotation: rotation
         });
         pathForLineWidth.updateTransform();
@@ -554,7 +556,7 @@ function createOrUpdateRepeatSymbols(
             updateAttr(path, null, makeTarget(index), symbolMeta, isUpdate);
         }
         else {
-            updateAttr(path, null, {scale: [0, 0]}, symbolMeta, isUpdate, function () {
+            updateAttr(path, null, { scaleX: 0, scaleY: 0 }, symbolMeta, isUpdate, function () {
                 bundle.remove(path);
             });
         }
@@ -575,11 +577,14 @@ function createOrUpdateRepeatSymbols(
         updateAttr(
             path,
             {
-                position: target.position,
-                scale: [0, 0]
+                x: target.x,
+                y: target.y,
+                scaleX: 0,
+                scaleY: 0
             },
             {
-                scale: target.scale,
+                scaleX: target.scaleX,
+                scaleY: target.scaleY,
                 rotation: target.rotation
             },
             symbolMeta,
@@ -606,8 +611,10 @@ function createOrUpdateRepeatSymbols(
         position[valueDim.index] = unit * (i - repeatTimes / 2 + 0.5) + pathPosition[valueDim.index];
 
         return {
-            position: position,
-            scale: symbolMeta.symbolScale.slice(),
+            x: position[0],
+            y: position[1],
+            scaleX: symbolMeta.symbolScale[0],
+            scaleY: symbolMeta.symbolScale[1],
             rotation: symbolMeta.rotation
         };
     }
@@ -641,12 +648,15 @@ function createOrUpdateSingleSymbol(
         updateAttr(
             mainPath,
             {
-                position: symbolMeta.pathPosition.slice(),
-                scale: [0, 0],
+                x: symbolMeta.pathPosition[0],
+                y: symbolMeta.pathPosition[1],
+                scaleX: 0,
+                scaleY: 0,
                 rotation: symbolMeta.rotation
             },
             {
-                scale: symbolMeta.symbolScale.slice()
+                scaleX: symbolMeta.symbolScale[0],
+                scaleY: symbolMeta.symbolScale[1]
             },
             symbolMeta,
             isUpdate
@@ -661,8 +671,10 @@ function createOrUpdateSingleSymbol(
             mainPath,
             null,
             {
-                position: symbolMeta.pathPosition.slice(),
-                scale: symbolMeta.symbolScale.slice(),
+                x: symbolMeta.pathPosition[0],
+                y: symbolMeta.pathPosition[1],
+                scaleX: symbolMeta.symbolScale[0],
+                scaleY: symbolMeta.symbolScale[1],
                 rotation: symbolMeta.rotation
             },
             symbolMeta,
@@ -767,17 +779,19 @@ function isAnimationEnabled(this: ItemModel) {
 function updateHoverAnimation(path: PictorialSymbol, symbolMeta: SymbolMeta) {
     path.off('emphasis').off('normal');
 
-    const scale = symbolMeta.symbolScale.slice();
+    const scale = symbolMeta.symbolScale;
 
     symbolMeta.hoverAnimation && path
         .on('emphasis', function () {
             this.animateTo({
-                scale: [scale[0] * 1.1, scale[1] * 1.1]
+                scaleX: scale[0] * 1.1,
+                scaleY: scale[1] * 1.1
             }, { duration: 400, easing: 'elasticOut' });
         })
         .on('normal', function () {
             this.animateTo({
-                scale: scale.slice()
+                scaleX: scale[0],
+                scaleY: scale[1]
             }, { duration: 400, easing: 'elasticOut' });
         });
 
@@ -790,7 +804,9 @@ function createBar(data: List, opt: CreateOpts, symbolMeta: SymbolMeta, isUpdate
     const bundle = new graphic.Group();
     bar.add(bundle);
     bar.__pictorialBundle = bundle;
-    bundle.attr('position', symbolMeta.bundlePosition.slice());
+
+    bundle.x = symbolMeta.bundlePosition[0];
+    bundle.y = symbolMeta.bundlePosition[1];
 
     if (symbolMeta.symbolRepeat) {
         createOrUpdateRepeatSymbols(bar, opt, symbolMeta);
@@ -815,7 +831,10 @@ function updateBar(bar: PictorialBarElement, opt: CreateOpts, symbolMeta: Symbol
     const bundle = bar.__pictorialBundle;
 
     graphic.updateProps(
-        bundle, {position: symbolMeta.bundlePosition.slice()}, animationModel, dataIndex
+        bundle, {
+            x: symbolMeta.bundlePosition[0],
+            y: symbolMeta.bundlePosition[1]
+        }, animationModel, dataIndex
     );
 
     if (symbolMeta.symbolRepeat) {
@@ -848,7 +867,7 @@ function removeBar(
 
     zrUtil.each(pathes, function (path) {
         graphic.updateProps(
-            path, {scale: [0, 0]}, animationModel, dataIndex,
+            path, { scaleX: 0, scaleY: 0 }, animationModel, dataIndex,
             function () {
                 bar.parent && bar.parent.remove(bar);
             }
@@ -879,8 +898,8 @@ function eachPath<Ctx>(
 
 function updateAttr<T extends Element>(
     el: T,
-    immediateAttrs: any,
-    animationAttrs: any,
+    immediateAttrs: PathProps,
+    animationAttrs: PathProps,
     symbolMeta: SymbolMeta,
     isUpdate?: boolean,
     cb?: () => void
diff --git a/src/chart/funnel/FunnelView.ts b/src/chart/funnel/FunnelView.ts
index f01234f..123b190 100644
--- a/src/chart/funnel/FunnelView.ts
+++ b/src/chart/funnel/FunnelView.ts
@@ -177,7 +177,8 @@ class FunnelPiece extends graphic.Group {
 
         labelText.attr({
             rotation: labelLayout.rotation,
-            origin: [labelLayout.x, labelLayout.y],
+            originX: labelLayout.x,
+            originY: labelLayout.y,
             z2: 10
         });
 
diff --git a/src/chart/graph/GraphView.ts b/src/chart/graph/GraphView.ts
index d19fd8a..c73f0b1 100644
--- a/src/chart/graph/GraphView.ts
+++ b/src/chart/graph/GraphView.ts
@@ -155,8 +155,8 @@ class GraphView extends ChartView {
 
         if (isViewCoordSys(coordSys)) {
             const groupNewProp = {
-                position: coordSys.position,
-                scale: coordSys.scale
+                x: coordSys.x, y: coordSys.y,
+                scaleX: coordSys.scaleX, scaleY: coordSys.scaleY
             };
             if (this._firstRender) {
                 group.attr(groupNewProp);
@@ -198,7 +198,7 @@ class GraphView extends ChartView {
                             && this._startForceLayoutIteration(forceLayout, layoutAnimation);
                         forceLayout.setFixed(idx);
                         // Write position back to layout
-                        data.setItemLayout(idx, el.position);
+                        data.setItemLayout(idx, [el.x, el.y]);
                     }
                 }).on('dragend', () => {
                     if (forceLayout) {
@@ -443,10 +443,9 @@ class GraphView extends ChartView {
         const data = seriesModel.getData();
 
         const nodeScale = getNodeGlobalScale(seriesModel);
-        const invScale = [nodeScale, nodeScale];
 
         data.eachItemGraphicEl(function (el, idx) {
-            el.attr('scale', invScale);
+            el.scaleX = el.scaleY = nodeScale;
         });
     }
 
diff --git a/src/chart/graph/graphHelper.ts b/src/chart/graph/graphHelper.ts
index 362ce9d..745a79b 100644
--- a/src/chart/graph/graphHelper.ts
+++ b/src/chart/graph/graphHelper.ts
@@ -29,8 +29,7 @@ export function getNodeGlobalScale(seriesModel: GraphSeriesModel) {
 
     const nodeScaleRatio = seriesModel.option.nodeScaleRatio;
 
-    const groupScale = coordSys.scale;
-    const groupZoom = (groupScale && groupScale[0]) || 1;
+    const groupZoom = coordSys.scaleX;
     // Scale node when zoom changes
     const roamZoom = coordSys.getZoom();
     const nodeScale = (roamZoom - 1) * nodeScaleRatio + 1;
diff --git a/src/chart/helper/EffectLine.ts b/src/chart/helper/EffectLine.ts
index 65e555f..bf20ab9 100644
--- a/src/chart/helper/EffectLine.ts
+++ b/src/chart/helper/EffectLine.ts
@@ -92,10 +92,10 @@ class EffectLine extends graphic.Group {
         symbol.setStyle('shadowColor', color);
         symbol.setStyle(effectModel.getItemStyle(['color']));
 
-        symbol.attr('scale', size);
+        symbol.scaleX = size[0];
+        symbol.scaleY = size[1];
 
         symbol.setColor(color);
-        symbol.attr('scale', size);
 
         this._symbolType = symbolType;
         this._symbolScale = size;
@@ -194,8 +194,8 @@ class EffectLine extends graphic.Group {
         const p2 = symbol.__p2;
         const cp1 = symbol.__cp1;
         const t = symbol.__t;
-        const pos = symbol.position;
-        const lastPos = [pos[0], pos[1]];
+        const pos = [symbol.x, symbol.y];
+        const lastPos = pos.slice();
         const quadraticAt = curveUtil.quadraticAt;
         const quadraticDerivativeAt = curveUtil.quadraticDerivativeAt;
         pos[0] = quadraticAt(p1[0], cp1[0], p2[0], t);
@@ -209,8 +209,7 @@ class EffectLine extends graphic.Group {
         // enable continuity trail for 'line', 'rect', 'roundRect' symbolType
         if (this._symbolType === 'line' || this._symbolType === 'rect' || this._symbolType === 'roundRect') {
             if (symbol.__lastT !== undefined && symbol.__lastT < symbol.__t) {
-                const scaleY = vec2.dist(lastPos, pos) * 1.05;
-                symbol.attr('scale', [symbol.scale[0], scaleY]);
+                symbol.scaleY = vec2.dist(lastPos, pos) * 1.05;
                 // make sure the last segment render within endPoint
                 if (t === 1) {
                     pos[0] = lastPos[0] + (pos[0] - lastPos[0]) / 2;
@@ -219,15 +218,16 @@ class EffectLine extends graphic.Group {
             }
             else if (symbol.__lastT === 1) {
                 // After first loop, symbol.__t does NOT start with 0, so connect p1 to pos directly.
-                const scaleY = 2 * vec2.dist(p1, pos);
-                symbol.attr('scale', [symbol.scale[0], scaleY ]);
+                symbol.scaleY = 2 * vec2.dist(p1, pos);
             }
             else {
-                symbol.attr('scale', this._symbolScale);
+                symbol.scaleY = this._symbolScale[1];
             }
         }
         symbol.__lastT = symbol.__t;
         symbol.ignore = false;
+        symbol.x = pos[0];
+        symbol.y = pos[1];
     }
 
 
diff --git a/src/chart/helper/EffectPolyline.ts b/src/chart/helper/EffectPolyline.ts
index 11ad927..2211b59 100644
--- a/src/chart/helper/EffectPolyline.ts
+++ b/src/chart/helper/EffectPolyline.ts
@@ -100,13 +100,14 @@ class EffectPolyline extends EffectLine {
             frame = Math.min(frame - 1, len - 2);
         }
 
-        vec2.lerp(
-            symbol.position, points[frame], points[frame + 1],
-            (t - offsets[frame]) / (offsets[frame + 1] - offsets[frame])
-        );
-
-        const tx = points[frame + 1][0] - points[frame][0];
-        const ty = points[frame + 1][1] - points[frame][1];
+        const p = (t - offsets[frame]) / (offsets[frame + 1] - offsets[frame]);
+        const p0 = points[frame];
+        const p1 = points[frame + 1];
+        symbol.x = p0[0] * (1 - p) + p * p1[0];
+        symbol.y = p0[1] * (1 - p) + p * p1[1];
+
+        const tx = p1[0] - p0[0];
+        const ty = p1[1] - p0[1];
         symbol.rotation = -Math.atan2(ty, tx) - Math.PI / 2;
 
         this._lastFrame = frame;
diff --git a/src/chart/helper/EffectSymbol.ts b/src/chart/helper/EffectSymbol.ts
index 4abf1de..17381ad 100644
--- a/src/chart/helper/EffectSymbol.ts
+++ b/src/chart/helper/EffectSymbol.ts
@@ -101,7 +101,8 @@ class EffectSymbol extends Group {
                 },
                 z2: 99,
                 silent: true,
-                scale: [0.5, 0.5]
+                scaleX: 0.5,
+                scaleY: 0.5
             });
 
             const delay = -i / EFFECT_RIPPLE_NUMBER * effectCfg.period + effectCfg.effectOffset;
@@ -174,7 +175,7 @@ class EffectSymbol extends Group {
         const symbolSize = normalizeSymbolSize(data.getItemVisual(idx, 'symbolSize'));
         const color = data.getItemVisual(idx, 'color');
 
-        rippleGroup.attr('scale', symbolSize);
+        rippleGroup.setScale(symbolSize);
 
         rippleGroup.traverse(function (ripplePath: Path) {
             ripplePath.setStyle('fill', color);
@@ -182,9 +183,8 @@ class EffectSymbol extends Group {
 
         const symbolOffset = itemModel.getShallow('symbolOffset');
         if (symbolOffset) {
-            const pos = rippleGroup.position;
-            pos[0] = parsePercent(symbolOffset[0], symbolSize[0]);
-            pos[1] = parsePercent(symbolOffset[1], symbolSize[1]);
+            rippleGroup.x = parsePercent(symbolOffset[0], symbolSize[0]);
+            rippleGroup.y = parsePercent(symbolOffset[1], symbolSize[1]);
         }
         rippleGroup.rotation = (itemModel.getShallow('symbolRotate') || 0) * Math.PI / 180 || 0;
 
diff --git a/src/chart/helper/Line.ts b/src/chart/helper/Line.ts
index c1c7ee6..ca3bc51 100644
--- a/src/chart/helper/Line.ts
+++ b/src/chart/helper/Line.ts
@@ -333,8 +333,8 @@ class Line extends graphic.Group {
         let invScale = 1;
         let parentNode = this.parent;
         while (parentNode) {
-            if (parentNode.scale) {
-                invScale /= parentNode.scale[0];
+            if (parentNode.scaleX) {
+                invScale /= parentNode.scaleX;
             }
             parentNode = parentNode.parent;
         }
@@ -354,29 +354,30 @@ class Line extends graphic.Group {
         vector.normalize(d, d);
 
         if (symbolFrom) {
-            symbolFrom.attr('position', fromPos);
+            symbolFrom.setPosition(fromPos);
             const tangent = line.tangentAt(0);
-            symbolFrom.attr('rotation', Math.PI / 2 - Math.atan2(
+            symbolFrom.rotation = Math.PI / 2 - Math.atan2(
                 tangent[1], tangent[0]
-            ));
-            symbolFrom.attr('scale', [invScale * percent, invScale * percent]);
+            );
+            symbolFrom.scaleX = symbolFrom.scaleY = invScale * percent;
+            symbolFrom.markRedraw();
         }
         if (symbolTo) {
-            symbolTo.attr('position', toPos);
+            symbolTo.setPosition(toPos);
             const tangent = line.tangentAt(1);
-            symbolTo.attr('rotation', -Math.PI / 2 - Math.atan2(
+            symbolTo.rotation = -Math.PI / 2 - Math.atan2(
                 tangent[1], tangent[0]
-            ));
-            symbolTo.attr('scale', [invScale * percent, invScale * percent]);
+            );
+            symbolTo.scaleX = symbolTo.scaleY = invScale * percent;
+            symbolTo.markRedraw();
         }
 
         if (!label.ignore) {
-            label.attr('position', toPos);
+            label.x = label.y = 0;
+            label.originX = label.originY = 0;
 
-            let textPosition;
             let textAlign: ZRTextAlign;
             let textVerticalAlign: ZRTextVerticalAlign;
-            let textOrigin;
 
             const distance = label.__labelDistance;
             const distanceX = distance[0] * invScale;
@@ -396,7 +397,7 @@ class Line extends graphic.Group {
                 if (toPos[0] < fromPos[0]) {
                     rotation = Math.PI + rotation;
                 }
-                label.attr('rotation', rotation);
+                label.rotation = rotation;
             }
 
             let dy;
@@ -423,13 +424,15 @@ class Line extends graphic.Group {
 
             switch (label.__position) {
                 case 'end':
-                    textPosition = [d[0] * distanceX + toPos[0], d[1] * distanceY + toPos[1]];
+                    label.x = d[0] * distanceX + toPos[0];
+                    label.y = d[1] * distanceY + toPos[1];
                     textAlign = d[0] > 0.8 ? 'left' : (d[0] < -0.8 ? 'right' : 'center');
                     textVerticalAlign = d[1] > 0.8 ? 'top' : (d[1] < -0.8 ? 'bottom' : 'middle');
                     break;
 
                 case 'start':
-                    textPosition = [-d[0] * distanceX + fromPos[0], -d[1] * distanceY + fromPos[1]];
+                    label.x = -d[0] * distanceX + fromPos[0];
+                    label.y = -d[1] * distanceY + fromPos[1];
                     textAlign = d[0] > 0.8 ? 'right' : (d[0] < -0.8 ? 'left' : 'center');
                     textVerticalAlign = d[1] > 0.8 ? 'bottom' : (d[1] < -0.8 ? 'top' : 'middle');
                     break;
@@ -437,38 +440,39 @@ class Line extends graphic.Group {
                 case 'insideStartTop':
                 case 'insideStart':
                 case 'insideStartBottom':
-                    textPosition = [distanceX * dir + fromPos[0], fromPos[1] + dy];
+                    label.x = distanceX * dir + fromPos[0];
+                    label.y = fromPos[1] + dy;
                     textAlign = tangent[0] < 0 ? 'right' : 'left';
-                    textOrigin = [-distanceX * dir, -dy];
+                    label.originX = -distanceX * dir;
+                    label.originY = -dy;
                     break;
 
                 case 'insideMiddleTop':
                 case 'insideMiddle':
                 case 'insideMiddleBottom':
                 case 'middle':
-                    textPosition = [cp[0], cp[1] + dy];
+                    label.x = cp[0];
+                    label.y = cp[1] + dy;
                     textAlign = 'center';
-                    textOrigin = [0, -dy];
+                    label.originY = -dy;
                     break;
 
                 case 'insideEndTop':
                 case 'insideEnd':
                 case 'insideEndBottom':
-                    textPosition = [-distanceX * dir + toPos[0], toPos[1] + dy];
+                    label.x = -distanceX * dir + toPos[0];
+                    label.y = toPos[1] + dy;
                     textAlign = tangent[0] >= 0 ? 'right' : 'left';
-                    textOrigin = [distanceX * dir, -dy];
+                    label.originX = distanceX * dir;
+                    label.originY = -dy;
                     break;
             }
 
-            label.attr({
-                style: {
-                    // Use the user specified text align and baseline first
-                    verticalAlign: label.__verticalAlign || textVerticalAlign,
-                    align: label.__align || textAlign
-                },
-                position: textPosition,
-                scale: [invScale, invScale],
-                origin: textOrigin
+            label.scaleX = label.scaleY = invScale;
+            label.setStyle({
+                // Use the user specified text align and baseline first
+                verticalAlign: label.__verticalAlign || textVerticalAlign,
+                align: label.__align || textAlign
             });
         }
     }
diff --git a/src/chart/helper/Symbol.ts b/src/chart/helper/Symbol.ts
index 56be9d8..ddffc18 100644
--- a/src/chart/helper/Symbol.ts
+++ b/src/chart/helper/Symbol.ts
@@ -35,8 +35,6 @@ const normalLabelAccessPath = ['label'] as const;
 const emphasisLabelAccessPath = ['emphasis', 'label'] as const;
 
 type ECSymbol = ReturnType<typeof createSymbol> & {
-    __symbolOriginalScale: number[]
-    __z2Origin: number
     highDownOnUpdate(fromState: DisplayState, toState: DisplayState): void
 };
 
@@ -46,6 +44,14 @@ class Symbol extends graphic.Group {
 
     private _symbolType: string;
 
+    /**
+     * Original scale
+     */
+    private _scaleX: number;
+    private _scaleY: number;
+
+    private _z2: number;
+
     constructor(data: List, idx: number, seriesScope?: SymbolDrawSeriesScope) {
         super();
         this.updateData(data, idx, seriesScope);
@@ -76,7 +82,8 @@ class Symbol extends graphic.Group {
         symbolPath.attr({
             z2: 100,
             culling: true,
-            scale: getScale(symbolSize)
+            scaleX: symbolSize[0] / 2,
+            scaleY: symbolSize[1] / 2
         });
         // Rewrite drift method
         symbolPath.drift = driftSymbol;
@@ -112,7 +119,12 @@ class Symbol extends graphic.Group {
      * Including the change caused by animation
      */
     getScale() {
-        return this.childAt(0).scale;
+        const symbolPath = this.childAt(0);
+        return [symbolPath.scaleX, symbolPath.scaleY];
+    }
+
+    getOriginalScale() {
+        return [this._scaleX, this._scaleY];
     }
 
     /**
@@ -164,7 +176,8 @@ class Symbol extends graphic.Group {
             const symbolPath = this.childAt(0) as ECSymbol;
             symbolPath.silent = false;
             graphic.updateProps(symbolPath, {
-                scale: getScale(symbolSize)
+                scaleX: symbolSize[0] / 2,
+                scaleY: symbolSize[1] / 2
             }, seriesModel, idx);
         }
 
@@ -175,13 +188,14 @@ class Symbol extends graphic.Group {
             const fadeIn = seriesScope && seriesScope.fadeIn;
 
             const target: PathProps = {
-                scale: symbolPath.scale.slice()
+                scaleX: symbolPath.scaleX,
+                scaleY: symbolPath.scaleY
             };
             fadeIn && (target.style = {
                 opacity: symbolPath.style.opacity
             });
 
-            symbolPath.scale = [0, 0];
+            symbolPath.scaleX = symbolPath.scaleY = 0;
             fadeIn && (symbolPath.style.opacity = 0);
 
             graphic.initProps(symbolPath, target, seriesModel, idx);
@@ -251,10 +265,8 @@ class Symbol extends graphic.Group {
         symbolPath.attr('rotation', (symbolRotate || 0) * Math.PI / 180 || 0);
 
         if (symbolOffset) {
-            symbolPath.attr('position', [
-                parsePercent(symbolOffset[0], symbolSize[0]),
-                parsePercent(symbolOffset[1], symbolSize[1])
-            ]);
+            symbolPath.x = parsePercent(symbolOffset[0], symbolSize[0]);
+            symbolPath.y = parsePercent(symbolOffset[1], symbolSize[1]);
         }
 
         cursorStyle && symbolPath.attr('cursor', cursorStyle);
@@ -270,16 +282,16 @@ class Symbol extends graphic.Group {
         }
 
         const liftZ = data.getItemVisual(idx, 'liftZ');
-        const z2Origin = symbolPath.__z2Origin;
+        const z2Origin = this._z2;
         if (liftZ != null) {
             if (z2Origin == null) {
-                symbolPath.__z2Origin = symbolPath.z2;
+                this._z2 = symbolPath.z2;
                 symbolPath.z2 += liftZ;
             }
         }
         else if (z2Origin != null) {
             symbolPath.z2 = z2Origin;
-            symbolPath.__z2Origin = null;
+            this._z2 = null;
         }
 
         const useNameLabel = seriesScope && seriesScope.useNameLabel;
@@ -299,7 +311,8 @@ class Symbol extends graphic.Group {
             return useNameLabel ? data.getName(idx) : getDefaultLabel(data, idx);
         }
 
-        symbolPath.__symbolOriginalScale = getScale(symbolSize);
+        this._scaleX = symbolSize[0] / 2;
+        this._scaleY = symbolSize[1] / 2;
         symbolPath.highDownOnUpdate = (
             hoverAnimation && seriesModel.isAnimationEnabled()
         ) ? highDownOnUpdate : null;
@@ -322,7 +335,8 @@ class Symbol extends graphic.Group {
                 style: {
                     opacity: 0
                 },
-                scale: [0, 0]
+                scaleX: 0,
+                scaleY: 0
             },
             this._seriesModel,
             graphic.getECData(this).dataIndex,
@@ -345,14 +359,12 @@ function highDownOnUpdate(this: ECSymbol, fromState: DisplayState, toState: Disp
         return;
     }
 
+    const scale = (this.parent as Symbol).getOriginalScale();
     if (toState === 'emphasis') {
-        const scale = this.__symbolOriginalScale;
         const ratio = scale[1] / scale[0];
         const emphasisOpt = {
-            scale: [
-                Math.max(scale[0] * 1.1, scale[0] + 3),
-                Math.max(scale[1] * 1.1, scale[1] + 3 * ratio)
-            ]
+            x: Math.max(scale[0] * 1.1, scale[0] + 3),
+            y: Math.max(scale[1] * 1.1, scale[1] + 3 * ratio)
         };
         // FIXME
         // modify it after support stop specified animation.
@@ -362,15 +374,12 @@ function highDownOnUpdate(this: ECSymbol, fromState: DisplayState, toState: Disp
     }
     else if (toState === 'normal') {
         this.animateTo({
-            scale: this.__symbolOriginalScale
+            scaleX: scale[0],
+            scaleY: scale[1]
         }, { duration: 400, easing: 'elasticOut' });
     }
 }
 
-function getScale(symbolSize: number[]) {
-    return [symbolSize[0] / 2, symbolSize[1] / 2];
-}
-
 function driftSymbol(this: ECSymbol, dx: number, dy: number) {
     this.parent.drift(dx, dy);
 }
diff --git a/src/chart/helper/SymbolDraw.ts b/src/chart/helper/SymbolDraw.ts
index 3480e35..7eea47e 100644
--- a/src/chart/helper/SymbolDraw.ts
+++ b/src/chart/helper/SymbolDraw.ts
@@ -161,7 +161,7 @@ class SymbolDraw {
                 const point = data.getItemLayout(newIdx) as number[];
                 if (symbolNeedsDraw(data, point, newIdx, opt)) {
                     const symbolEl = new SymbolCtor(data, newIdx, seriesScope);
-                    symbolEl.attr('position', point);
+                    symbolEl.setPosition(point);
                     data.setItemGraphicEl(newIdx, symbolEl);
                     group.add(symbolEl);
                 }
@@ -175,12 +175,13 @@ class SymbolDraw {
                 }
                 if (!symbolEl) {
                     symbolEl = new SymbolCtor(data, newIdx);
-                    symbolEl.attr('position', point);
+                    symbolEl.setPosition(point);
                 }
                 else {
                     symbolEl.updateData(data, newIdx, seriesScope);
                     graphic.updateProps(symbolEl, {
-                        position: point
+                        x: point[0],
+                        y: point[1]
                     }, seriesModel);
                 }
 
@@ -210,7 +211,8 @@ class SymbolDraw {
             // Not use animation
             data.eachItemGraphicEl(function (el, idx) {
                 const point = data.getItemLayout(idx);
-                el.attr('position', point);
+                el.setPosition(point);
+                el.markRedraw();
             });
         }
     };
@@ -237,7 +239,7 @@ class SymbolDraw {
             if (symbolNeedsDraw(data, point, idx, opt)) {
                 const el = new this._SymbolCtor(data, idx, this._seriesScope);
                 el.traverse(updateIncrementalAndHover);
-                el.attr('position', point);
+                el.setPosition(point);
                 this.group.add(el);
                 data.setItemGraphicEl(idx, el);
             }
diff --git a/src/chart/line/LineView.ts b/src/chart/line/LineView.ts
index 63e4c56..a2e27bb 100644
--- a/src/chart/line/LineView.ts
+++ b/src/chart/line/LineView.ts
@@ -590,7 +590,7 @@ class LineView extends ChartView {
                     return;
                 }
                 symbol = new SymbolClz(data, dataIndex);
-                symbol.position = pt;
+                symbol.setPosition(pt);
                 symbol.setZ(
                     seriesModel.get('zlevel'),
                     seriesModel.get('z')
@@ -721,12 +721,19 @@ class LineView extends ChartView {
             next = turnPointsIntoStep(diff.next, coordSys, step);
             stackedOnNext = turnPointsIntoStep(diff.stackedOnNext, coordSys, step);
         }
+
+        // if (next.length < polyline.shape.points.length) {
+        //     debugger
+        // }
+
         // `diff.current` is subset of `current` (which should be ensured by
         // turnPointsIntoStep), so points in `__points` can be updated when
         // points in `current` are update during animation.
         (polyline.shape as any).__points = diff.current;
         polyline.shape.points = current;
 
+        // Stop previous animation.
+        polyline.stopAnimation();
         graphic.updateProps(polyline, {
             shape: {
                 points: next
@@ -738,6 +745,7 @@ class LineView extends ChartView {
                 points: current,
                 stackedOnPoints: stackedOnCurrent
             });
+            polygon.stopAnimation();
             graphic.updateProps(polygon, {
                 shape: {
                     points: next,
@@ -769,7 +777,8 @@ class LineView extends ChartView {
             polyline.animators[0].during(function () {
                 for (let i = 0; i < updatedDataInfo.length; i++) {
                     const el = updatedDataInfo[i].el;
-                    el.attr('position', (polyline.shape as any).__points[updatedDataInfo[i].ptIdx]);
+                    el.setPosition((polyline.shape as any).__points[updatedDataInfo[i].ptIdx]);
+                    el.markRedraw();
                 }
             });
         }
diff --git a/src/chart/pie/PieView.ts b/src/chart/pie/PieView.ts
index 386d07d..3e02c4f 100644
--- a/src/chart/pie/PieView.ts
+++ b/src/chart/pie/PieView.ts
@@ -73,16 +73,17 @@ function toggleItemSelected(
     const dy = Math.sin(midAngle);
 
     const offset = isSelected ? selectedOffset : 0;
-    const position = [dx * offset, dy * offset];
+    const obj = {
+        x: dx * offset,
+        y: dy * offset
+    };
 
     hasAnimation
         // animateTo will stop revious animation like update transition
         ? el.animate()
-            .when(200, {
-                position: position
-            })
+            .when(200, obj)
             .start('bounceOut')
-        : el.attr('position', position);
+        : el.attr(obj);
 }
 
 /**
@@ -298,7 +299,8 @@ class PiePiece extends graphic.Group {
 
         labelText.attr({
             rotation: labelLayout.rotation,
-            origin: [labelLayout.x, labelLayout.y],
+            originX: labelLayout.x,
+            originY: labelLayout.y,
             z2: 10
         });
 
diff --git a/src/chart/radar/RadarView.ts b/src/chart/radar/RadarView.ts
index 804fb2a..b96deb0 100644
--- a/src/chart/radar/RadarView.ts
+++ b/src/chart/radar/RadarView.ts
@@ -69,7 +69,8 @@ class RadarView extends ChartView {
                     strokeNoScale: true
                 },
                 z2: 100,
-                scale: [symbolSize[0] / 2, symbolSize[1] / 2]
+                scaleX: symbolSize[0] / 2,
+                scaleY: symbolSize[1] / 2
             });
             return symbolPath as RadarSymbol;
         }
@@ -89,15 +90,16 @@ class RadarView extends ChartView {
                 if (symbolPath) {
                     symbolPath.__dimIdx = i;
                     if (oldPoints[i]) {
-                        symbolPath.attr('position', oldPoints[i]);
+                        symbolPath.setPosition(oldPoints[i]);
                         graphic[isInit ? 'initProps' : 'updateProps'](
                             symbolPath, {
-                                position: newPoints[i]
+                                x: newPoints[i][0],
+                                y: newPoints[i][1]
                             }, seriesModel, idx
                         );
                     }
                     else {
-                        symbolPath.attr('position', newPoints[i]);
+                        symbolPath.setPosition(newPoints[i]);
                     }
                     symbolGroup.add(symbolPath);
                 }
diff --git a/src/chart/sankey/SankeyView.ts b/src/chart/sankey/SankeyView.ts
index 4785914..492358b 100644
--- a/src/chart/sankey/SankeyView.ts
+++ b/src/chart/sankey/SankeyView.ts
@@ -194,7 +194,8 @@ class SankeyView extends ChartView {
 
         group.removeAll();
 
-        group.attr('position', [layoutInfo.x, layoutInfo.y]);
+        group.x = layoutInfo.x;
+        group.y = layoutInfo.y;
 
         // generate a bezire Curve for each edge
         graph.eachEdge(function (edge) {
diff --git a/src/chart/sunburst/SunburstPiece.ts b/src/chart/sunburst/SunburstPiece.ts
index dba74f5..4b526ea 100644
--- a/src/chart/sunburst/SunburstPiece.ts
+++ b/src/chart/sunburst/SunburstPiece.ts
@@ -296,9 +296,8 @@ class SunburstPiece extends graphic.Group {
             opacity: getLabelAttr('opacity')
         });
 
-        const textX = r * dx + layout.cx;
-        const textY = r * dy + layout.cy;
-        label.attr('position', [textX, textY]);
+        label.x = r * dx + layout.cx;
+        label.y = r * dy + layout.cy;
 
         const rotateType = getLabelAttr('rotate');
         let rotate = 0;
diff --git a/src/chart/themeRiver/ThemeRiverView.ts b/src/chart/themeRiver/ThemeRiverView.ts
index 5f7b875..3fa1a8d 100644
--- a/src/chart/themeRiver/ThemeRiverView.ts
+++ b/src/chart/themeRiver/ThemeRiverView.ts
@@ -49,7 +49,8 @@ class ThemeRiverView extends ChartView {
         const rect = layoutInfo.rect;
         const boundaryGap = layoutInfo.boundaryGap;
 
-        group.attr('position', [0, rect.y + boundaryGap[0]]);
+        group.x = 0;
+        group.y = rect.y + boundaryGap[0];
 
         function keyGetter(item: LayerSeries[number]) {
             return item.name;
diff --git a/src/chart/tree/TreeView.ts b/src/chart/tree/TreeView.ts
index 391220d..580510a 100644
--- a/src/chart/tree/TreeView.ts
+++ b/src/chart/tree/TreeView.ts
@@ -184,10 +184,12 @@ class TreeView extends ChartView {
         const layout = seriesModel.get('layout');
 
         if (layout === 'radial') {
-            group.attr('position', [layoutInfo.x + layoutInfo.width / 2, layoutInfo.y + layoutInfo.height / 2]);
+            group.x = layoutInfo.x + layoutInfo.width / 2;
+            group.y = layoutInfo.y + layoutInfo.height / 2;
         }
         else {
-            group.attr('position', [layoutInfo.x, layoutInfo.y]);
+            group.x = layoutInfo.x;
+            group.y = layoutInfo.y;
         }
 
         this._updateViewCoordSys(seriesModel);
@@ -294,8 +296,10 @@ class TreeView extends ChartView {
 
         // Here we use viewCoordSys just for computing the 'position' and 'scale' of the group
         this.group.attr({
-            position: viewCoordSys.position,
-            scale: viewCoordSys.scale
+            x: viewCoordSys.x,
+            y: viewCoordSys.y,
+            scaleX: viewCoordSys.scaleX,
+            scaleY: viewCoordSys.scaleY
         });
 
         this._viewCoordSys = viewCoordSys;
@@ -351,10 +355,9 @@ class TreeView extends ChartView {
         const data = seriesModel.getData();
 
         const nodeScale = this._getNodeGlobalScale(seriesModel);
-        const invScale = [nodeScale, nodeScale];
 
         data.eachItemGraphicEl(function (el, idx) {
-            el.attr('scale', invScale);
+            el.scaleX = el.scaleY = nodeScale;
         });
     }
 
@@ -366,8 +369,7 @@ class TreeView extends ChartView {
 
         const nodeScaleRatio = this._nodeScaleRatio;
 
-        const groupScale = coordSys.scale;
-        const groupZoom = (groupScale && groupScale[0]) || 1;
+        const groupZoom = coordSys.scaleX || 1;
         // Scale node when zoom changes
         const roamZoom = coordSys.getZoom();
         const nodeScale = (roamZoom - 1) * nodeScaleRatio + 1;
@@ -435,8 +437,8 @@ function updateNode(
     const sourceLayout = source.getLayout() as TreeNodeLayout;
     const sourceOldLayout = sourceSymbolEl
         ? {
-            x: sourceSymbolEl.position[0],
-            y: sourceSymbolEl.position[1],
+            x: sourceSymbolEl.x,
+            y: sourceSymbolEl.y,
             rawX: sourceSymbolEl.__radialOldRawX,
             rawY: sourceSymbolEl.__radialOldRawY
         }
@@ -445,7 +447,8 @@ function updateNode(
 
     if (isInit) {
         symbolEl = new SymbolClz(data, dataIndex, seriesScope) as TreeSymbol;
-        symbolEl.attr('position', [sourceOldLayout.x, sourceOldLayout.y]);
+        symbolEl.x = sourceOldLayout.x;
+        symbolEl.y = sourceOldLayout.y;
     }
     else {
         symbolEl.updateData(data, dataIndex, seriesScope);
@@ -459,7 +462,8 @@ function updateNode(
     group.add(symbolEl);
     data.setItemGraphicEl(dataIndex, symbolEl);
     graphic.updateProps(symbolEl, {
-        position: [targetLayout.x, targetLayout.y]
+        x: targetLayout.x,
+        y: targetLayout.y
     }, seriesModel);
 
     const symbolPath = symbolEl.getSymbolPath();
@@ -616,7 +620,8 @@ function removeNode(
     }
 
     graphic.updateProps(symbolEl, {
-        position: [sourceLayout.x + 1, sourceLayout.y + 1]
+        x: sourceLayout.x + 1,
+        y: sourceLayout.y + 1
     }, seriesModel, function () {
         group.remove(symbolEl);
         data.setItemGraphicEl(dataIndex, null);
@@ -686,14 +691,14 @@ function getEdgeShape(seriesScope: TreeSeriesScope, sourceLayout: TreeNodeLayout
         const radialCoor4 = radialCoordinate(x2, y2);
 
         return {
-            x1: radialCoor1.x,
-            y1: radialCoor1.y,
-            x2: radialCoor4.x,
-            y2: radialCoor4.y,
-            cpx1: radialCoor2.x,
-            cpy1: radialCoor2.y,
-            cpx2: radialCoor3.x,
-            cpy2: radialCoor3.y
+            x1: radialCoor1.x || 0,
+            y1: radialCoor1.y || 0,
+            x2: radialCoor4.x || 0,
+            y2: radialCoor4.y || 0,
+            cpx1: radialCoor2.x || 0,
+            cpy1: radialCoor2.y || 0,
+            cpx2: radialCoor3.x || 0,
+            cpy2: radialCoor3.y || 0
         };
     }
     else {
diff --git a/src/chart/treemap/TreemapView.ts b/src/chart/treemap/TreemapView.ts
index e94ffad..5ecb9a8 100644
--- a/src/chart/treemap/TreemapView.ts
+++ b/src/chart/treemap/TreemapView.ts
@@ -121,7 +121,8 @@ interface ReRoot {
 }
 
 interface LastCfg {
-    oldPos?: graphic.Group['position']
+    oldX?: number
+    oldY?: number
     oldShape?: graphic.Rect['shape']
     fadein: boolean
 }
@@ -218,7 +219,8 @@ class TreemapView extends ChartView {
             this._initEvents(containerGroup);
             this.group.add(containerGroup);
         }
-        containerGroup.attr('position', [layoutInfo.x, layoutInfo.y]);
+        containerGroup.x = layoutInfo.x;
+        containerGroup.y = layoutInfo.y;
 
         return containerGroup;
     }
@@ -403,7 +405,7 @@ class TreemapView extends ChartView {
                     }
 
                     target = storageName === 'nodeGroup'
-                        ? {position: [targetX, targetY], style: {opacity: 0}}
+                        ? {x: targetX, y: targetY, style: {opacity: 0}}
                         : {
                             shape: {x: targetX, y: targetY, width: 0, height: 0},
                             style: {opacity: 0}
@@ -425,9 +427,11 @@ class TreemapView extends ChartView {
                 }
 
                 if (el instanceof graphic.Group) {
-                    if (last.oldPos) {
-                        target.position = el.position.slice();
-                        el.attr('position', last.oldPos);
+                    if (last.oldX != null) {
+                        target.x = el.x;
+                        target.y = el.y;
+                        el.x = last.oldX;
+                        el.y = last.oldY;
                     }
                 }
                 else {
@@ -799,7 +803,8 @@ function renderNode(
 
     parentGroup.add(group);
     // x,y are not set when el is above view root.
-    group.attr('position', [thisLayout.x || 0, thisLayout.y || 0]);
+    group.x = thisLayout.x || 0;
+    group.y = thisLayout.y || 0;
     inner(group).nodeWidth = thisWidth;
     inner(group).nodeHeight = thisHeight;
 
@@ -1013,7 +1018,8 @@ function renderNode(
     function prepareAnimationWhenHasOld(lasts: LastCfg[], element: graphic.Group | graphic.Rect) {
         const lastCfg = lasts[thisRawIndex] = {} as LastCfg;
         if (element instanceof Group) {
-            lastCfg.oldPos = element.position.slice();
+            lastCfg.oldX = element.x;
+            lastCfg.oldY = element.y;
         }
         else {
             lastCfg.oldShape = extend({}, element.shape);
@@ -1042,7 +1048,8 @@ function renderNode(
             // When no parent old shape found, its parent is new too,
             // so we can just use {x:0, y:0}.
             if (isGroup) {
-                lastCfg.oldPos = [0, parentOldY];
+                lastCfg.oldX = 0;
+                lastCfg.oldY = parentOldY;
             }
             else {
                 lastCfg.oldShape = {x: parentOldX, y: parentOldY, width: 0, height: 0};
diff --git a/src/component/axis/AxisBuilder.ts b/src/component/axis/AxisBuilder.ts
index c9b4bf7..940b6ac 100644
--- a/src/component/axis/AxisBuilder.ts
+++ b/src/component/axis/AxisBuilder.ts
@@ -154,7 +154,8 @@ class AxisBuilder {
 
         // FIXME Not use a seperate text group?
         const transformGroup = new graphic.Group({
-            position: opt.position.slice(),
+            x: opt.position[0],
+            y: opt.position[1],
             rotation: opt.rotation
         });
 
@@ -325,14 +326,11 @@ const builders: Record<'axisLine' | 'axisTickLabel' | 'axisName', AxisElementsBu
 
                     // Calculate arrow position with offset
                     const r = point.r + point.offset;
-                    const pos = [
-                        pt1[0] + r * Math.cos(opt.rotation),
-                        pt1[1] - r * Math.sin(opt.rotation)
-                    ];
 
                     symbol.attr({
                         rotation: point.rotate,
-                        position: pos,
+                        x: pt1[0] + r * Math.cos(opt.rotation),
+                        y: pt1[1] - r * Math.sin(opt.rotation),
                         silent: true,
                         z2: 11
                     });
@@ -425,7 +423,8 @@ const builders: Record<'axisLine' | 'axisTickLabel' | 'axisName', AxisElementsBu
         formatterParams[mainType + 'Index' as AxisIndexKey] = axisModel.componentIndex;
 
         const textEl = new graphic.Text({
-            position: pos,
+            x: pos[0],
+            y: pos[1],
             rotation: labelLayout.rotation,
             silent: AxisBuilder.isLabelSilent(axisModel),
             style: graphic.createTextStyle(textStyleModel, {
@@ -767,13 +766,10 @@ function buildAxisLabel(
             || axisModel.get(['axisLine', 'lineStyle', 'color']);
 
         const tickCoord = axis.dataToCoord(tickValue);
-        const pos = [
-            tickCoord,
-            opt.labelOffset + opt.labelDirection * labelMargin
-        ];
 
         const textEl = new graphic.Text({
-            position: pos,
+            x: tickCoord,
+            y: opt.labelOffset + opt.labelDirection * labelMargin,
             rotation: labelLayout.rotation,
             silent: silent,
             z2: 10,
diff --git a/src/component/axis/ParallelAxisView.ts b/src/component/axis/ParallelAxisView.ts
index 2ec93d1..d83a55c 100644
--- a/src/component/axis/ParallelAxisView.ts
+++ b/src/component/axis/ParallelAxisView.ts
@@ -143,7 +143,8 @@ class ParallelAxisView extends ComponentView {
             .mount({
                 enableGlobalPan: true,
                 rotation: builderOpt.rotation,
-                position: builderOpt.position
+                x: builderOpt.position[0],
+                y: builderOpt.position[1]
             })
             .setPanels([{
                 panelId: 'pl',
diff --git a/src/component/axisPointer/BaseAxisPointer.ts b/src/component/axisPointer/BaseAxisPointer.ts
index 2617e21..a57d205 100644
--- a/src/component/axisPointer/BaseAxisPointer.ts
+++ b/src/component/axisPointer/BaseAxisPointer.ts
@@ -26,7 +26,6 @@ import * as throttleUtil from '../../util/throttle';
 import {makeInner} from '../../util/model';
 import { AxisPointer } from './AxisPointer';
 import { AxisBaseModel } from '../../coord/AxisBaseModel';
-import { VectorArray } from 'zrender/src/core/vector';
 import ExtensionAPI from '../../ExtensionAPI';
 import Displayable, { DisplayableProps } from 'zrender/src/graphic/Displayable';
 import Element from 'zrender/src/Element';
@@ -45,7 +44,8 @@ const bind = zrUtil.bind;
 
 type Icon = ReturnType<typeof graphic.createIcon>;
 interface Transform {
-    position: VectorArray,
+    x: number,
+    y: number,
     rotation: number
 }
 
@@ -323,7 +323,8 @@ class BaseAxisPointer implements AxisPointer {
                 // be used on shape, otherwise the effect will be weird.
                 // TODOTODO
                 // shape: elOption.label.shape,
-                position: elOption.label.position
+                x: elOption.label.x,
+                y: elOption.label.y
             });
 
             updateLabelShowHide(labelEl, axisPointerModel);
@@ -383,7 +384,8 @@ class BaseAxisPointer implements AxisPointer {
         if (!zrUtil.isArray(handleSize)) {
             handleSize = [handleSize, handleSize];
         }
-        (handle as graphic.Path).attr('scale', [handleSize[0] / 2, handleSize[1] / 2]);
+        handle.scaleX = handleSize[0] / 2;
+        handle.scaleY = handleSize[1] / 2;
 
         throttleUtil.createOrUpdate(
             this,
@@ -549,7 +551,8 @@ function updateLabelShowHide(labelEl: Element, axisPointerModel: AxisPointerMode
 
 function getHandleTransProps(trans: Transform): Transform {
     return {
-        position: trans.position.slice(),
+        x: trans.x || 0,
+        y: trans.y || 0,
         rotation: trans.rotation || 0
     };
 }
diff --git a/src/component/axisPointer/CartesianAxisPointer.ts b/src/component/axisPointer/CartesianAxisPointer.ts
index 8377462..73f95dc 100644
--- a/src/component/axisPointer/CartesianAxisPointer.ts
+++ b/src/component/axisPointer/CartesianAxisPointer.ts
@@ -27,7 +27,6 @@ import { ScaleDataValue, VerticalAlign, HorizontalAlign, CommonAxisPointerOption
 import Grid from '../../coord/cartesian/Grid';
 import Axis2D from '../../coord/cartesian/Axis2D';
 import { PathProps } from 'zrender/src/graphic/Path';
-import { VectorArray } from 'zrender/src/core/vector';
 import Model from '../../model/Model';
 
 // Not use top level axisPointer model
@@ -81,9 +80,10 @@ class CartesianAxisPointer extends BaseAxisPointer {
         });
         // @ts-ignore
         layoutInfo.labelMargin = axisPointerModel.get(['handle', 'margin']);
+        const pos = viewHelper.getTransformedPosition(axisModel.axis, value, layoutInfo);
         return {
-            // @ts-ignore
-            position: viewHelper.getTransformedPosition(axisModel.axis, value, layoutInfo),
+            x: pos[0],
+            y: pos[1],
             rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0)
         };
     }
@@ -93,7 +93,7 @@ class CartesianAxisPointer extends BaseAxisPointer {
      */
     updateHandleTransform(
         transform: {
-            position: VectorArray,
+            x: number, y: number,
             rotation: number
         },
         delta: number[],
@@ -106,7 +106,7 @@ class CartesianAxisPointer extends BaseAxisPointer {
         const otherExtent = getCartesian(grid, axis).getOtherAxis(axis).getGlobalExtent();
         const dimIndex = axis.dim === 'x' ? 0 : 1;
 
-        const currPosition = transform.position;
+        const currPosition = [transform.x, transform.y];
         currPosition[dimIndex] += delta[dimIndex];
         currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]);
         currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]);
@@ -125,7 +125,8 @@ class CartesianAxisPointer extends BaseAxisPointer {
         ];
 
         return {
-            position: currPosition,
+            x: currPosition[0],
+            y: currPosition[1],
             rotation: transform.rotation,
             cursorPoint: cursorPoint,
             tooltipOption: tooltipOptions[dimIndex]
diff --git a/src/component/axisPointer/SingleAxisPointer.ts b/src/component/axisPointer/SingleAxisPointer.ts
index 7e2620c..345a67c 100644
--- a/src/component/axisPointer/SingleAxisPointer.ts
+++ b/src/component/axisPointer/SingleAxisPointer.ts
@@ -27,7 +27,6 @@ import { PathProps } from 'zrender/src/graphic/Path';
 import { ScaleDataValue, VerticalAlign, CommonAxisPointerOption } from '../../util/types';
 import ExtensionAPI from '../../ExtensionAPI';
 import SingleAxisModel from '../../coord/single/AxisModel';
-import { VectorArray } from 'zrender/src/core/vector';
 import Model from '../../model/Model';
 
 const XY = ['x', 'y'] as const;
@@ -83,8 +82,10 @@ class SingleAxisPointer extends BaseAxisPointer {
         const layoutInfo = singleAxisHelper.layout(axisModel, {labelInside: false});
         // @ts-ignore
         layoutInfo.labelMargin = axisPointerModel.get(['handle', 'margin']);
+        const position = viewHelper.getTransformedPosition(axisModel.axis, value, layoutInfo);
         return {
-            position: viewHelper.getTransformedPosition(axisModel.axis, value, layoutInfo),
+            x: position[0],
+            y: position[1],
             rotation: layoutInfo.rotation + (layoutInfo.labelDirection < 0 ? Math.PI : 0)
         };
     }
@@ -94,7 +95,8 @@ class SingleAxisPointer extends BaseAxisPointer {
      */
     updateHandleTransform(
         transform: {
-            position: VectorArray,
+            x: number,
+            y: number,
             rotation: number
         },
         delta: number[],
@@ -105,7 +107,7 @@ class SingleAxisPointer extends BaseAxisPointer {
         const coordSys = axis.coordinateSystem;
         const dimIndex = getPointDimIndex(axis);
         const axisExtent = getGlobalExtent(coordSys, dimIndex);
-        const currPosition = transform.position;
+        const currPosition = [transform.x, transform.y];
         currPosition[dimIndex] += delta[dimIndex];
         currPosition[dimIndex] = Math.min(axisExtent[1], currPosition[dimIndex]);
         currPosition[dimIndex] = Math.max(axisExtent[0], currPosition[dimIndex]);
@@ -115,7 +117,8 @@ class SingleAxisPointer extends BaseAxisPointer {
         cursorPoint[dimIndex] = currPosition[dimIndex];
 
         return {
-            position: currPosition,
+            x: currPosition[0],
+            y: currPosition[1],
             rotation: transform.rotation,
             cursorPoint: cursorPoint,
             tooltipOption: {
diff --git a/src/component/axisPointer/viewHelper.ts b/src/component/axisPointer/viewHelper.ts
index 1753ecf..1b6f639 100644
--- a/src/component/axisPointer/viewHelper.ts
+++ b/src/component/axisPointer/viewHelper.ts
@@ -119,7 +119,8 @@ export function buildLabelElOption(
 
     elOption.label = {
         // shape: {x: 0, y: 0, width: width, height: height, r: labelModel.get('borderRadius')},
-        position: position.slice(),
+        x: position[0],
+        y: position[1],
         // TODO: rich
         style: {
             text: text,
diff --git a/src/component/calendar/CalendarView.ts b/src/component/calendar/CalendarView.ts
index 2786b15..d4e0bc6 100644
--- a/src/component/calendar/CalendarView.ts
+++ b/src/component/calendar/CalendarView.ts
@@ -263,22 +263,23 @@ class CalendarView extends ComponentView {
         margin: number
     ): RichTextProps {
 
-        point = point.slice();
+        let x = point[0];
+        let y = point[1];
         let aligns: [ZRTextAlign, ZRTextVerticalAlign] = ['center', 'bottom'];
 
         if (position === 'bottom') {
-            point[1] += margin;
+            y += margin;
             aligns = ['center', 'top'];
         }
         else if (position === 'left') {
-            point[0] -= margin;
+            x -= margin;
         }
         else if (position === 'right') {
-            point[0] += margin;
+            x += margin;
             aligns = ['center', 'top'];
         }
         else { // top
-            point[1] -= margin;
+            y -= margin;
         }
 
         let rotate = 0;
@@ -288,7 +289,8 @@ class CalendarView extends ComponentView {
 
         return {
             rotation: rotate,
-            position: point,
+            x,
+            y,
             style: {
                 align: aligns[0],
                 verticalAlign: aligns[1]
diff --git a/src/component/dataZoom/SliderZoomView.ts b/src/component/dataZoom/SliderZoomView.ts
index 64e41ff..d49e9a8 100644
--- a/src/component/dataZoom/SliderZoomView.ts
+++ b/src/component/dataZoom/SliderZoomView.ts
@@ -238,18 +238,19 @@ class SliderZoomView extends DataZoomView {
         // Transform barGroup.
         barGroup.attr(
             (orient === HORIZONTAL && !inverse)
-            ? {scale: otherAxisInverse ? [1, 1] : [1, -1]}
+            ? {scaleY: otherAxisInverse ? 1 : -1, scaleX: 1 }
             : (orient === HORIZONTAL && inverse)
-            ? {scale: otherAxisInverse ? [-1, 1] : [-1, -1]}
+            ? {scaleY: otherAxisInverse ? 1 : -1, scaleX: -1 }
             : (orient === VERTICAL && !inverse)
-            ? {scale: otherAxisInverse ? [1, -1] : [1, 1], rotation: Math.PI / 2}
+            ? {scaleY: otherAxisInverse ? -1 : 1, scaleX: 1, rotation: Math.PI / 2}
             // Dont use Math.PI, considering shadow direction.
-            : {scale: otherAxisInverse ? [-1, -1] : [-1, 1], rotation: Math.PI / 2}
+            : {scaleY: otherAxisInverse ? -1 : 1, scaleX: -1, rotation: Math.PI / 2}
         );
 
         // Position barGroup
         const rect = thisGroup.getBoundingRect([barGroup]);
-        thisGroup.attr('position', [location.x - rect.x, location.y - rect.y]);
+        thisGroup.x = location.x - rect.x;
+        thisGroup.y = location.y - rect.y;
     }
 
     /**
@@ -569,8 +570,10 @@ class SliderZoomView extends DataZoomView {
             const handle = displaybles.handles[handleIndex];
             const handleHeight = this._handleHeight;
             (handle as graphic.Path).attr({
-                scale: [handleHeight / 2, handleHeight / 2],
-                position: [handleEnds[handleIndex], size[1] / 2 - handleHeight / 2]
+                scaleX: handleHeight / 2,
+                scaleY: handleHeight / 2,
+                x: handleEnds[handleIndex],
+                y: size[1] / 2 - handleHeight / 2
             });
         }, this);
 
diff --git a/src/component/helper/BrushController.ts b/src/component/helper/BrushController.ts
index 3ced929..5492254 100644
--- a/src/component/helper/BrushController.ts
+++ b/src/component/helper/BrushController.ts
@@ -338,9 +338,11 @@ class BrushController extends Eventful<BrushControllerEvents> {
 
     mount(opt?: {
         enableGlobalPan?: boolean;
-        position?: number[];
+        x?: number;
+        y?: number;
         rotation?: number;
-        scale?: number[];
+        scaleX?: number;
+        scaleY?: number
     }): BrushController {
         opt = opt || {};
 
@@ -354,9 +356,11 @@ class BrushController extends Eventful<BrushControllerEvents> {
         this._zr.add(thisGroup);
 
         thisGroup.attr({
-            position: opt.position || [0, 0],
+            x: opt.x || 0,
+            y: opt.y || 0,
             rotation: opt.rotation || 0,
-            scale: opt.scale || [1, 1]
+            scaleX: opt.scaleX || 1,
+            scaleY: opt.scaleY || 1
         });
         this._transform = thisGroup.getLocalTransform();
 
diff --git a/src/component/helper/MapDraw.ts b/src/component/helper/MapDraw.ts
index 1eb8808..4a653f7 100644
--- a/src/component/helper/MapDraw.ts
+++ b/src/component/helper/MapDraw.ts
@@ -139,9 +139,6 @@ class MapDraw {
         group.decomposeTransform();
         group.dirty();
 
-        const scale = transformInfo.rawScale;
-        const position = transformInfo.rawPosition;
-
         regionsGroup.removeAll();
 
         const itemStyleAccessPath = ['itemStyle'] as const;
@@ -195,10 +192,14 @@ class MapDraw {
                 }
             }
 
+            const sx = transformInfo.rawScaleX;
+            const sy = transformInfo.rawScaleY;
+            const offsetX = transformInfo.rawX;
+            const offsetY = transformInfo.rawY;
             const transformPoint = function (point: number[]): number[] {
                 return [
-                    point[0] * scale[0] + position[0],
-                    point[1] * scale[1] + position[1]
+                    point[0] * sx + offsetX,
+                    point[1] * sy + offsetY
                 ];
             };
 
@@ -258,13 +259,16 @@ class MapDraw {
                     labelFetcher = mapOrGeoModel;
                 }
 
+                const centerPt = transformPoint(region.center);
                 const textEl = new graphic.Text({
-                    position: transformPoint(region.center.slice()),
+                    x: centerPt[0],
+                    y: centerPt[1],
                     // FIXME
                     // label rotation is not support yet in geo or regions of series-map
                     // that has no data. The rotation will be effected by this `scale`.
                     // So needed to change to RectText?
-                    scale: [1 / group.scale[0], 1 / group.scale[1]],
+                    scaleX: 1 / group.scaleX,
+                    scaleY: 1 / group.scaleY,
                     z2: 10,
                     silent: true
                 });
@@ -390,10 +394,11 @@ class MapDraw {
             }));
 
             if (this._updateGroup) {
-                const scale = this.group.scale;
+                const group = this.group;
                 this._regionsGroup.traverse(function (el) {
                     if (el.type === 'text') {
-                        el.attr('scale', [1 / scale[0], 1 / scale[1]]);
+                        el.scaleX = 1 / group.scaleX;
+                        el.scaleY = 1 / group.scaleY;
                     }
                 });
             }
diff --git a/src/component/legend/LegendView.ts b/src/component/legend/LegendView.ts
index c836676..6783978 100644
--- a/src/component/legend/LegendView.ts
+++ b/src/component/legend/LegendView.ts
@@ -136,7 +136,8 @@ class LegendView extends ComponentView {
             viewportSize,
             padding
         );
-        this.group.attr('position', [layoutRect.x - mainRect.x, layoutRect.y - mainRect.y]);
+        this.group.x = layoutRect.x - mainRect.x;
+        this.group.y = layoutRect.y - mainRect.y;
 
         // Render background after group is layout.
         this.group.add(
@@ -483,6 +484,9 @@ class LegendView extends ComponentView {
         const contentRect = contentGroup.getBoundingRect();
         const contentPos = [-contentRect.x, -contentRect.y];
 
+        selectorGroup.markRedraw();
+        contentGroup.markRedraw();
+
         if (selector) {
             // Place buttons in selectorGroup
             layoutUtil.box(
@@ -510,8 +514,10 @@ class LegendView extends ComponentView {
 
             //Always align selector to content as 'middle'
             selectorPos[1 - orientIdx] += contentRect[hw] / 2 - selectorRect[hw] / 2;
-            selectorGroup.attr('position', selectorPos);
-            contentGroup.attr('position', contentPos);
+            selectorGroup.x = selectorPos[0];
+            selectorGroup.y = selectorPos[1];
+            contentGroup.x = contentPos[0];
+            contentGroup.y = contentPos[1];
 
             const mainRect = {x: 0, y: 0} as ZRRectLike;
             mainRect[wh] = contentRect[wh] + selectorButtonGap + selectorRect[wh];
@@ -520,7 +526,8 @@ class LegendView extends ComponentView {
             return mainRect;
         }
         else {
-            contentGroup.attr('position', contentPos);
+            contentGroup.x = contentPos[0];
+            contentGroup.y = contentPos[1];
             return this.group.getBoundingRect();
         }
     }
diff --git a/src/component/legend/ScrollableLegendView.ts b/src/component/legend/ScrollableLegendView.ts
index bdb004f..3a90998 100644
--- a/src/component/legend/ScrollableLegendView.ts
+++ b/src/component/legend/ScrollableLegendView.ts
@@ -223,7 +223,9 @@ class ScrollableLegendView extends LegendView {
             mainRect[hw] = Math.max(mainRect[hw], selectorRect[hw]);
             mainRect[yx] = Math.min(mainRect[yx], selectorRect[yx] + selectorPos[1 - orientIdx]);
 
-            selectorGroup.attr('position', selectorPos);
+            selectorGroup.x = selectorPos[0];
+            selectorGroup.y = selectorPos[1];
+            selectorGroup.markRedraw();
         }
 
         return mainRect;
@@ -267,7 +269,7 @@ class ScrollableLegendView extends LegendView {
         // If first rendering, `contentGroup.position` is [0, 0], which
         // does not make sense and may cause unexepcted animation if adopted.
         if (!isFirstRender) {
-            contentPos[orientIdx] = contentGroup.position[orientIdx];
+            contentPos[orientIdx] = contentGroup[yx];
         }
 
         // Layout container group based on 0.
@@ -293,9 +295,9 @@ class ScrollableLegendView extends LegendView {
         // Always align controller to content as 'middle'.
         controllerPos[1 - orientIdx] += contentRect[hw] / 2 - controllerRect[hw] / 2;
 
-        contentGroup.attr('position', contentPos);
-        containerGroup.attr('position', containerPos);
-        controllerGroup.attr('position', controllerPos);
+        contentGroup.setPosition(contentPos);
+        containerGroup.setPosition(containerPos);
+        controllerGroup.setPosition(controllerPos);
 
         // Calculate `mainRect` and set `clipPath`.
         // mainRect should not be calculated by `this.group.getBoundingRect()`
@@ -333,7 +335,7 @@ class ScrollableLegendView extends LegendView {
         const pageInfo = this._getPageInfo(legendModel);
         pageInfo.pageIndex != null && graphic.updateProps(
             contentGroup,
-            {position: pageInfo.contentPosition},
+            { x: pageInfo.contentPosition[0], y: pageInfo.contentPosition[1] },
             // When switch from "show controller" to "not show controller", view should be
             // updated immediately without animation, otherwise causes weird effect.
             showController ? legendModel : null
@@ -417,7 +419,7 @@ class ScrollableLegendView extends LegendView {
         const pCount = !itemCount ? 0 : 1;
 
         const result: PageInfo = {
-            contentPosition: zrUtil.slice(contentGroup.position),
+            contentPosition: [contentGroup.x, contentGroup.y],
             pageCount: pCount,
             pageIndex: pCount - 1,
             pagePrevDataIndex: null,
@@ -504,7 +506,7 @@ class ScrollableLegendView extends LegendView {
         function getItemInfo(el: Element): ItemInfo {
             if (el) {
                 const itemRect = el.getBoundingRect();
-                const start = itemRect[xy] + el.position[orientIdx];
+                const start = itemRect[xy] + el[xy];
                 return {
                     s: start,
                     e: start + itemRect[wh],
diff --git a/src/component/timeline/SliderTimelineView.ts b/src/component/timeline/SliderTimelineView.ts
index e97fa71..e8dc40d 100644
--- a/src/component/timeline/SliderTimelineView.ts
+++ b/src/component/timeline/SliderTimelineView.ts
@@ -276,8 +276,8 @@ class SliderTimelineView extends TimelineView {
         const mainBound = getBound(mainGroup.getBoundingRect());
         const labelBound = getBound(labelGroup.getBoundingRect());
 
-        const mainPosition = mainGroup.position;
-        const labelsPosition = labelGroup.position;
+        const mainPosition = [mainGroup.x, mainGroup.y];
+        const labelsPosition = [labelGroup.x, labelGroup.y];
 
         labelsPosition[0] = mainPosition[0] = viewBound[0][0];
 
@@ -294,19 +294,16 @@ class SliderTimelineView extends TimelineView {
             labelsPosition[1] = mainPosition[1] + labelPosOpt;
         }
 
-        mainGroup.attr('position', mainPosition);
-        labelGroup.attr('position', labelsPosition);
+        mainGroup.setPosition(mainPosition);
+        labelGroup.setPosition(labelsPosition);
         mainGroup.rotation = labelGroup.rotation = layoutInfo.rotation;
 
         setOrigin(mainGroup);
         setOrigin(labelGroup);
 
         function setOrigin(targetGroup: graphic.Group) {
-            const pos = targetGroup.position;
-            targetGroup.origin = [
-                viewBound[0][0] - pos[0],
-                viewBound[1][0] - pos[1]
-            ];
+            targetGroup.originX = viewBound[0][0] - targetGroup.x;
+            targetGroup.originY = viewBound[1][0] - targetGroup.y;
         }
 
         function getBound(rect: RectLike) {
@@ -442,7 +439,8 @@ class SliderTimelineView extends TimelineView {
             const hoverLabelModel = itemModel.getModel(['emphasis', 'label']);
             const tickCoord = axis.dataToCoord(labelItem.tickValue);
             const textEl = new graphic.Text({
-                position: [tickCoord, 0],
+                x: tickCoord,
+                y: 0,
                 rotation: layoutInfo.labelRotation - layoutInfo.rotation,
                 onclick: bind(this._changeTimeline, this, dataIndex),
                 silent: false,
@@ -576,8 +574,8 @@ class SliderTimelineView extends TimelineView {
         toCoord > axisExtent[1] && (toCoord = axisExtent[1]);
         toCoord < axisExtent[0] && (toCoord = axisExtent[0]);
 
-        this._currentPointer.position[0] = toCoord;
-        this._currentPointer.dirty();
+        this._currentPointer.x = toCoord;
+        this._currentPointer.markRedraw();
 
         const targetDataIndex = this._findNearestTick(toCoord);
         const timelineModel = this.model;
@@ -750,15 +748,16 @@ function giveSymbol(
     symbolSize = symbolSize instanceof Array
         ? symbolSize.slice()
         : [+symbolSize, +symbolSize];
-    symbolSize[0] /= 2;
-    symbolSize[1] /= 2;
-    opt.scale = symbolSize;
+
+    opt.scaleX = symbolSize[0] / 2;
+    opt.scaleY = symbolSize[1] / 2;
 
     const symbolOffset = hostModel.get('symbolOffset');
     if (symbolOffset) {
-        const pos = opt.position = opt.position || [0, 0];
-        pos[0] += numberUtil.parsePercent(symbolOffset[0], symbolSize[0]);
-        pos[1] += numberUtil.parsePercent(symbolOffset[1], symbolSize[1]);
+        opt.x = opt.x || 0;
+        opt.y = opt.y || 0;
+        opt.x += numberUtil.parsePercent(symbolOffset[0], symbolSize[0]);
+        opt.y += numberUtil.parsePercent(symbolOffset[1], symbolSize[1]);
     }
 
     const symbolRotate = hostModel.get('symbolRotate');
@@ -792,18 +791,20 @@ function pointerMoveTo(
     const toCoord = axis.dataToCoord(timelineModel.getData().get('value', dataIndex));
 
     if (noAnimation || !pointerModel.get('animation', true)) {
-        pointer.attr({position: [toCoord, 0]});
+        pointer.x = toCoord;
+        pointer.y = 0;
     }
     else {
         pointer.stopAnimation(true);
-        pointer.animateTo(
-            { position: [toCoord, 0] },
-            {
+        pointer.animateTo({
+            x: toCoord,
+            y: 0
+        }, {
                 duration: pointerModel.get('animationDuration', true),
                 easing: pointerModel.get('animationEasing', true)
-            }
-        );
+        });
     }
+    pointer.markRedraw();
 }
 
 
diff --git a/src/component/title.ts b/src/component/title.ts
index 942866e..c58d6ee 100644
--- a/src/component/title.ts
+++ b/src/component/title.ts
@@ -241,7 +241,8 @@ class TitleView extends ComponentView {
             textVerticalAlign = textVerticalAlign || 'top';
         }
 
-        group.attr('position', [layoutRect.x, layoutRect.y]);
+        group.x = layoutRect.x;
+        group.y = layoutRect.y;
         const alignStyle = {
             align: textAlign,
             verticalAlign: textVerticalAlign
diff --git a/src/component/toolbox/ToolboxView.ts b/src/component/toolbox/ToolboxView.ts
index 4380416..2c50dba 100644
--- a/src/component/toolbox/ToolboxView.ts
+++ b/src/component/toolbox/ToolboxView.ts
@@ -296,8 +296,8 @@ class ToolboxView extends ComponentView {
                 const rect = textContain.getBoundingRect(
                     titleText, RichText.makeFont(emphasisTextStyle)
                 );
-                const offsetX = icon.position[0] + group.position[0];
-                const offsetY = icon.position[1] + group.position[1] + itemSize;
+                const offsetX = icon.x + group.x;
+                const offsetY = icon.y + group.y + itemSize;
 
                 let needPutOnTop = false;
                 if (offsetY + rect.height > api.getHeight()) {
diff --git a/src/component/tooltip/TooltipRichContent.ts b/src/component/tooltip/TooltipRichContent.ts
index f4dcff2..950e31c 100644
--- a/src/component/tooltip/TooltipRichContent.ts
+++ b/src/component/tooltip/TooltipRichContent.ts
@@ -152,8 +152,11 @@ class TooltipRichContent {
     }
 
     moveTo(x: number, y: number) {
-        if (this.el) {
-            this.el.attr('position', [x, y]);
+        const el = this.el;
+        if (el) {
+            el.x = x;
+            el.y = y;
+            el.markRedraw();
         }
     }
 
diff --git a/src/component/tooltip/TooltipView.ts b/src/component/tooltip/TooltipView.ts
index bcade6e..3ddcc77 100644
--- a/src/component/tooltip/TooltipView.ts
+++ b/src/component/tooltip/TooltipView.ts
@@ -305,7 +305,8 @@ class TooltipView extends ComponentView {
 
         if (payload.tooltip && payload.x != null && payload.y != null) {
             const el = proxyRect as ECElement;
-            el.position = [payload.x, payload.y];
+            el.x = payload.x;
+            el.y = payload.y;
             el.update();
             el.tooltip = payload.tooltip;
             // Manually show tooltip while view is not using zrender elements.
diff --git a/src/component/visualMap/ContinuousModel.ts b/src/component/visualMap/ContinuousModel.ts
index 4217412..ab27dca 100644
--- a/src/component/visualMap/ContinuousModel.ts
+++ b/src/component/visualMap/ContinuousModel.ts
@@ -263,7 +263,8 @@ class ContinuousModel extends VisualMapModel<ContinousVisualMapOption> {
     static defaultOption = inheritDefaultOption(VisualMapModel.defaultOption, {
         align: 'auto',           // 'auto', 'left', 'right', 'top', 'bottom'
         calculable: false,
-        hoverLink: true
+        hoverLink: true,
+        realtime: true
     }) as ContinousVisualMapOption;
 }
 
diff --git a/src/component/visualMap/ContinuousView.ts b/src/component/visualMap/ContinuousView.ts
index b65e166..7cafb0b 100644
--- a/src/component/visualMap/ContinuousView.ts
+++ b/src/component/visualMap/ContinuousView.ts
@@ -238,7 +238,7 @@ class ContinuousView extends VisualMapView {
             onDrift,
             onDragEnd
         );
-        handleThumb.position[0] = itemSize[0];
+        handleThumb.x = itemSize[0];
         barGroup.add(handleThumb);
 
         // Text is always horizontal layout but should not be effected by
@@ -284,7 +284,7 @@ class ContinuousView extends VisualMapView {
         orient: Orient
     ) {
         const indicator = createPolygon([[0, 0]], 'move');
-        indicator.position[0] = itemSize[0];
+        indicator.x = itemSize[0];
         indicator.attr({invisible: true, silent: true});
         barGroup.add(indicator);
 
@@ -508,12 +508,12 @@ class ContinuousView extends VisualMapView {
 
         return new graphic.Group(
             (orient === 'horizontal' && !inverse)
-            ? {scale: itemAlign === 'bottom' ? [1, 1] : [-1, 1], rotation: Math.PI / 2}
+            ? {scaleX: itemAlign === 'bottom' ? 1 : -1, rotation: Math.PI / 2}
             : (orient === 'horizontal' && inverse)
-            ? {scale: itemAlign === 'bottom' ? [-1, 1] : [1, 1], rotation: -Math.PI / 2}
+            ? {scaleX: itemAlign === 'bottom' ? -1 : 1, rotation: -Math.PI / 2}
             : (orient === 'vertical' && !inverse)
-            ? {scale: itemAlign === 'left' ? [1, -1] : [-1, -1]}
-            : {scale: itemAlign === 'left' ? [1, 1] : [-1, 1]}
+            ? {scaleX: itemAlign === 'left' ? 1 : -1, scaleY: -1}
+            : {scaleX: itemAlign === 'left' ? 1 : -1}
         );
     }
 
@@ -530,7 +530,7 @@ class ContinuousView extends VisualMapView {
         each([0, 1], function (handleIndex) {
             const handleThumb = handleThumbs[handleIndex];
             handleThumb.setStyle('fill', visualInRange.handlesColor[handleIndex]);
-            handleThumb.position[1] = handleEnds[handleIndex];
+            handleThumb.y = handleEnds[handleIndex];
 
             // Update handle label position.
             const textPoint = graphic.applyTransform(
@@ -570,7 +570,7 @@ class ContinuousView extends VisualMapView {
             return;
         }
 
-        indicator.position[1] = pos;
+        indicator.y = pos;
         indicator.attr('invisible', false);
         indicator.setShape('points', createIndicatorPoints(
             !!rangeSymbol, halfHoverLinkSize, pos, itemSize[1]
diff --git a/src/coord/View.ts b/src/coord/View.ts
index b19eaaf..259938a 100644
--- a/src/coord/View.ts
+++ b/src/coord/View.ts
@@ -164,12 +164,11 @@ class View extends Transformable implements CoordinateSystemMaster, CoordinateSy
         center = vector.applyTransform([], center, rawTransformMatrix);
         defaultCenter = vector.applyTransform([], defaultCenter, rawTransformMatrix);
 
-        roamTransform.origin = center;
-        roamTransform.position = [
-            defaultCenter[0] - center[0],
-            defaultCenter[1] - center[1]
-        ];
-        roamTransform.scale = [zoom, zoom];
+        roamTransform.originX = center[0];
+        roamTransform.originY = center[1];
+        roamTransform.x = defaultCenter[0] - center[0];
+        roamTransform.y = defaultCenter[1] - center[1];
+        roamTransform.scaleX = roamTransform.scaleY = zoom;
 
         this._updateTransform();
     }
@@ -195,17 +194,15 @@ class View extends Transformable implements CoordinateSystemMaster, CoordinateSy
         this.decomposeTransform();
     }
 
-    getTransformInfo(): {
-        roamTransform: matrix.MatrixArray,
-        rawScale: number[],
-        rawPosition: number[]
-    } {
+    getTransformInfo() {
         const roamTransform = this._roamTransformable.transform;
         const rawTransformable = this._rawTransformable;
         return {
             roamTransform: roamTransform ? zrUtil.slice(roamTransform) : matrix.create(),
-            rawScale: zrUtil.slice(rawTransformable.scale),
-            rawPosition: zrUtil.slice(rawTransformable.position)
+            rawScaleX: rawTransformable.scaleX,
+            rawScaleY: rawTransformable.scaleY,
+            rawX: rawTransformable.x,
+            rawY: rawTransformable.y
         };
     }
 
diff --git a/src/coord/geo/Geo.ts b/src/coord/geo/Geo.ts
index 0d40265..9627ba1 100644
--- a/src/coord/geo/Geo.ts
+++ b/src/coord/geo/Geo.ts
@@ -102,8 +102,7 @@ class Geo extends View {
         rawTransformable.decomposeTransform();
 
         if (invertLongitute) {
-            const scale = rawTransformable.scale;
-            scale[1] = -scale[1];
+            rawTransformable.scaleY = -rawTransformable.scaleY;
         }
 
         rawTransformable.updateTransform();
diff --git a/src/coord/geo/geoSVGLoader.ts b/src/coord/geo/geoSVGLoader.ts
index 62e0ac5..349dbb7 100644
--- a/src/coord/geo/geoSVGLoader.ts
+++ b/src/coord/geo/geoSVGLoader.ts
@@ -137,8 +137,9 @@ function buildGraphic(
         const elRoot = root;
         root = new Group();
         root.add(elRoot);
-        elRoot.scale = viewBoxTransform.scale;
-        elRoot.position = viewBoxTransform.position;
+        elRoot.scaleX = elRoot.scaleY = viewBoxTransform.scale;
+        elRoot.x = viewBoxTransform.x;
+        elRoot.y = viewBoxTransform.y;
     }
 
     root.setClipPath(new Rect({
diff --git a/src/util/graphic.ts b/src/util/graphic.ts
index cd0fac9..94474b6 100644
--- a/src/util/graphic.ts
+++ b/src/util/graphic.ts
@@ -364,8 +364,8 @@ function singleEnterEmphasis(el: Displayable) {
     }
 
     const emphasisStyle = el.states.emphasis.style;
-    const currentFill = el.style.fill;
-    const currentStroke = el.style.stroke;
+    const currentFill = el.style && el.style.fill;
+    const currentStroke = el.style && el.style.stroke;
 
     el.useState('emphasis');
 
@@ -520,8 +520,12 @@ export function setAsHighDownDispatcher(el: Element, asDispatcher: boolean) {
     const extendedEl = el as ExtendedElement;
     // Make `highDownSilentOnTouch` and `highDownOnUpdate` only work after
     // `setAsHighDownDispatcher` called. Avoid it is modified by user unexpectedly.
-    extendedEl.__highDownSilentOnTouch = (el as ECElement).highDownSilentOnTouch;
-    extendedEl.__highDownOnUpdate = (el as ECElement).highDownOnUpdate;
+    if ((el as ECElement).highDownSilentOnTouch) {
+        extendedEl.__highDownSilentOnTouch = (el as ECElement).highDownSilentOnTouch;
+    }
+    if ((el as ECElement).highDownOnUpdate) {
+        extendedEl.__highDownOnUpdate = (el as ECElement).highDownOnUpdate;
+    }
 
     // Simple optimize, since this method might be
     // called for each elements of a group in some cases.
@@ -1207,7 +1211,8 @@ export function groupTransition(
     }
     function getAnimatableProps(el: Displayable) {
         const obj: PathProps = {
-            position: vector.clone(el.position),
+            x: el.x,
+            y: el.y,
             rotation: el.rotation
         };
         if (isPath(el)) {
diff --git a/src/util/layout.ts b/src/util/layout.ts
index 0a28715..c18e990 100644
--- a/src/util/layout.ts
+++ b/src/util/layout.ts
@@ -73,7 +73,6 @@ function boxLayout(
     let currentLineMaxSize = 0;
 
     group.eachChild(function (child, idx) {
-        const position = child.position;
         const rect = child.getBoundingRect();
         const nextChild = group.childAt(idx + 1);
         const nextChildRect = nextChild && nextChild.getBoundingRect();
@@ -115,8 +114,8 @@ function boxLayout(
             return;
         }
 
-        position[0] = x;
-        position[1] = y;
+        child.x = x;
+        child.y = y;
 
         orient === 'horizontal'
             ? (x = nextX + gap)
@@ -379,11 +378,18 @@ export function positionElement(
     // Because 'tranlate' is the last step in transform
     // (see zrender/core/Transformable#getLocalTransform),
     // we can just only modify el.position to get final result.
-    const elPos = el.position;
     const dx = h ? layoutRect.x - rect.x : 0;
     const dy = v ? layoutRect.y - rect.y : 0;
 
-    el.attr('position', boundingMode === 'raw' ? [dx, dy] : [elPos[0] + dx, elPos[1] + dy]);
+    if (boundingMode === 'raw') {
+        el.x = dx;
+        el.y = dy;
+    }
+    else {
+        el.x += dx;
+        el.y += dy;
+    }
+    el.markRedraw();
 }
 
 /**
diff --git a/test/pie.html b/test/pie.html
index 62b1446..124ad41 100644
--- a/test/pie.html
+++ b/test/pie.html
@@ -51,14 +51,11 @@ under the License.
 
                 // Pencil sketch texture
                 var patternSrc = window.pieTexture;
-                var img = new Image();
-                img.src = patternSrc;
-
                 var itemStyle = {
                     normal: {
                         opacity: 0.7,
                         color: {
-                            image: img,
+                            image: patternSrc,
                             repeat: 'repeat'
                         },
                         borderWidth: 3,


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@echarts.apache.org
For additional commands, e-mail: commits-help@echarts.apache.org