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 2019/01/03 20:45:01 UTC

[incubator-echarts] branch master updated (78223af -> 2b5a3ee)

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

sushuang pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git.


    from 78223af  Sort of hoverStyle and highlight/downplay API
     new 9847233  Enhance highlight downplay API, support keep highlighted when change style (e.g., setOption or hoverLink).
     new 2b5a3ee  Fix highlight conflict.

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/chart/helper/Symbol.js                |  51 ++++-----
 src/chart/pie/PieView.js                  |  44 ++++----
 src/component/visualMap/ContinuousView.js |   6 +-
 src/component/visualMap/PiecewiseModel.js |   2 +-
 src/component/visualMap/PiecewiseView.js  |   5 +-
 src/component/visualMap/helper.js         |   9 +-
 src/util/graphic.js                       | 178 ++++++++++++++++--------------
 src/view/Chart.js                         |  25 +++--
 test/hoverStyle.html                      | 104 ++++++++++++++---
 9 files changed, 256 insertions(+), 168 deletions(-)


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


[incubator-echarts] 01/02: Enhance highlight downplay API, support keep highlighted when change style (e.g., setOption or hoverLink).

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git

commit 9847233b23a4d5bf65b01ebfb7d5547edb0e11cb
Author: sushuang <su...@gmail.com>
AuthorDate: Fri Jan 4 00:43:56 2019 +0800

    Enhance highlight downplay API, support keep highlighted when change style (e.g., setOption or hoverLink).
---
 src/chart/helper/Symbol.js |  51 ++++++++---------
 src/chart/pie/PieView.js   |  44 +++++++-------
 src/util/graphic.js        | 140 +++++++++++++++++++++------------------------
 test/hoverStyle.html       | 103 ++++++++++++++++++++++++++++-----
 4 files changed, 198 insertions(+), 140 deletions(-)

diff --git a/src/chart/helper/Symbol.js b/src/chart/helper/Symbol.js
index 347c4ce..0e74631 100644
--- a/src/chart/helper/Symbol.js
+++ b/src/chart/helper/Symbol.js
@@ -321,47 +321,44 @@ symbolProto._updateCommon = function (data, idx, symbolSize, seriesScope) {
         return useNameLabel ? data.getName(idx) : getDefaultLabel(data, idx);
     }
 
-    graphic.removeExtraHighDownEffect(symbolPath);
-
+    symbolPath.__symbolOriginalScale = getScale(symbolSize);
     symbolPath.hoverStyle = hoverItemStyle;
+    symbolPath.highDownOnUpdate = (
+        hoverAnimation && seriesModel.isAnimationEnabled()
+    ) ? highDownOnUpdate : null;
 
-    // FIXME
-    // Do not use symbol.trigger('emphasis'), but use symbol.highlight() instead.
     graphic.setHoverStyle(symbolPath);
-
-    symbolPath.__symbolOriginalScale = getScale(symbolSize);
-
-    if (hoverAnimation && seriesModel.isAnimationEnabled()) {
-        graphic.setExtraHighDownEffect(symbolPath, onEmphasis, onNormal);
-    }
 };
 
-function onEmphasis() {
+function highDownOnUpdate(fromState, toState) {
     // Do not support this hover animation util some scenario required.
     // Animation can only be supported in hover layer when using `el.incremetal`.
     if (this.incremental || this.useHoverLayer) {
         return;
     }
-    var scale = this.__symbolOriginalScale;
-    var ratio = scale[1] / scale[0];
-    this.animateTo({
-        scale: [
-            Math.max(scale[0] * 1.1, scale[0] + 3),
-            Math.max(scale[1] * 1.1, scale[1] + 3 * ratio)
-        ]
-    }, 400, 'elasticOut');
-}
 
-function onNormal() {
-    if (this.incremental || this.useHoverLayer) {
-        return;
+    if (toState === 'emphasis') {
+        var scale = this.__symbolOriginalScale;
+        var ratio = scale[1] / scale[0];
+        var emphasisOpt = {
+            scale: [
+                Math.max(scale[0] * 1.1, scale[0] + 3),
+                Math.max(scale[1] * 1.1, scale[1] + 3 * ratio)
+            ]
+        };
+        // FIXME
+        // modify it after support stop specified animation.
+        // toState === fromState
+        //     ? (this.stopAnimation(), this.attr(emphasisOpt))
+        this.animateTo(emphasisOpt, 400, 'elasticOut');
+    }
+    else if (toState === 'normal') {
+        this.animateTo({
+            scale: this.__symbolOriginalScale
+        }, 400, 'elasticOut');
     }
-    this.animateTo({
-        scale: this.__symbolOriginalScale
-    }, 400, 'elasticOut');
 }
 
-
 /**
  * @param {Function} cb
  * @param {Object} [opt]
diff --git a/src/chart/pie/PieView.js b/src/chart/pie/PieView.js
index 9913838..8cac16d 100644
--- a/src/chart/pie/PieView.js
+++ b/src/chart/pie/PieView.js
@@ -179,30 +179,30 @@ piePieceProto.updateData = function (data, idx, firstCreate) {
         seriesModel.get('animation')
     );
 
-    function onEmphasis() {
-        // Sector may has animation of updating data. Force to move to the last frame
-        // Or it may stopped on the wrong shape
-        sector.stopAnimation(true);
-        sector.animateTo({
-            shape: {
-                r: layout.r + seriesModel.get('hoverOffset')
+    this._updateLabel(data, idx);
+
+    this.highDownOnUpdate = (itemModel.get('hoverAnimation') && seriesModel.isAnimationEnabled())
+        ? function (fromState, toState) {
+            if (toState === 'emphasis') {
+                // Sector may has animation of updating data. Force to move to the last frame
+                // Or it may stopped on the wrong shape
+                sector.stopAnimation(true);
+                sector.animateTo({
+                    shape: {
+                        r: layout.r + seriesModel.get('hoverOffset')
+                    }
+                }, 300, 'elasticOut');
             }
-        }, 300, 'elasticOut');
-    }
-    function onNormal() {
-        sector.stopAnimation(true);
-        sector.animateTo({
-            shape: {
-                r: layout.r
+            else {
+                sector.stopAnimation(true);
+                sector.animateTo({
+                    shape: {
+                        r: layout.r
+                    }
+                }, 300, 'elasticOut');
             }
-        }, 300, 'elasticOut');
-    }
-    graphic.removeExtraHighDownEffect(sector);
-    if (itemModel.get('hoverAnimation') && seriesModel.isAnimationEnabled()) {
-        graphic.setExtraHighDownEffect(sector, onEmphasis, onNormal);
-    }
-
-    this._updateLabel(data, idx);
+        }
+        : null;
 
     graphic.setHoverStyle(this);
 };
diff --git a/src/util/graphic.js b/src/util/graphic.js
index 14db581..4e3e583 100644
--- a/src/util/graphic.js
+++ b/src/util/graphic.js
@@ -50,6 +50,10 @@ var mathMin = Math.min;
 var EMPTY_OBJ = {};
 var Z2_LIFT_VALUE = 1;
 
+var EMPHASIS = 'emphasis';
+var NORMAL = 'normal';
+
+
 /**
  * Extend shape with parameters
  */
@@ -269,7 +273,7 @@ function singleEnterEmphasis(el) {
     el.__highlighted = useHoverLayer ? 'layer' : 'plain';
 
     var zr = el.__zr;
-    if (!zr && useHoverLayer) {
+    if (el.isGroup || (!zr && useHoverLayer)) {
         return;
     }
 
@@ -317,8 +321,6 @@ function singleEnterEmphasis(el) {
         el.dirty(false);
         el.z2 += Z2_LIFT_VALUE;
     }
-
-    el.__extraOnEmphasis && el.__extraOnEmphasis();
 }
 
 function setDefaultHoverFillStroke(targetStyle, hoverStyle, prop) {
@@ -336,10 +338,14 @@ function singleEnterNormal(el) {
 
     el.__highlighted = false;
 
+    if (el.isGroup) {
+        return;
+    }
+
     if (highlighted === 'layer') {
         el.__zr && el.__zr.removeHover(el);
     }
-    else if (highlighted) {
+    else {
         var style = el.style;
 
         var normalStl = el.__cachedNormalStl;
@@ -358,16 +364,23 @@ function singleEnterNormal(el) {
             el.z2 = normalZ2;
         }
     }
-
-    el.__extraOnNormal && el.__extraOnNormal();
 }
 
-function traverseCall(el, method) {
-    el.isGroup
-        ? el.traverse(function (child) {
-            !child.isGroup && method(child);
-        })
-        : method(el);
+function traverseUpdate(el, updater, commonParam) {
+    // If root is group, also enter updater for `highDownOnUpdate`.
+    var fromState = NORMAL;
+    var toState = NORMAL;
+    var trigger;
+    // See the rule of `highDownOnUpdate` on `graphic.setAsHighDownDispatcher`.
+    el.__highlighted && (fromState = EMPHASIS, trigger = true);
+    updater(el, commonParam);
+    el.__highlighted && (toState = EMPHASIS, trigger = true);
+
+    el.isGroup && el.traverse(function (child) {
+        !child.isGroup && updater(child, commonParam);
+    });
+
+    trigger && el.__highDownOnUpdate && el.__highDownOnUpdate(fromState, toState);
 }
 
 /**
@@ -377,7 +390,9 @@ function traverseCall(el, method) {
  * to the `el`. See the reason on `setHoverStyle`.
  *
  * @param {module:zrender/Element} el Should not be `zrender/container/Group`.
- * @param {Function} [el.highDownTransition] Called when transit.
+ * @param {Object} [el.hoverStyle] Can be set on el or its descendants,
+ *        e.g., `el.hoverStyle = ...; graphic.setHoverStyle(el); `.
+ *        Often used when item group has a label element and it's hoverStyle is different.
  * @param {Object|boolean} [hoverStl] The specified hover style.
  *        If set as `false`, disable the hover style.
  *        Similarly, The `el.hoverStyle` can alse be set
@@ -387,7 +402,7 @@ function traverseCall(el, method) {
 export function setElementHoverStyle(el, hoverStl) {
     // For performance consideration, it might be better to make the "hover style" only the
     // difference properties from the "normal style", but not a entire copy of all styles.
-    hoverStl = el.__hoverStl = hoverStl !== false && (hoverStl || {});
+    hoverStl = el.__hoverStl = hoverStl !== false && (el.hoverStyle || hoverStl || {});
     el.__hoverStlDirty = true;
 
     // FIXME
@@ -412,64 +427,32 @@ export function setElementHoverStyle(el, hoverStl) {
     }
 }
 
-/**
- * Set customized emphasis effect like scale, animation.
- * If calling this method more than once with the same
- * `onEmphasis` and `onNormal` function, it will not
- * add but do nothing.
- *
- * Highlight that triggered by API (
- *     `chart.dispatchAction({type: 'highlight'});`
- *     or `el.trigger('emphasis');`
- * ) has higher priority than that triggered by `mouseover`.
- * When element has been called to be entered emphasis, mouse over
- * should not trigger the highlight effect (for example, animation
- * scale) again, and `mouseout` should not downplay the highlight
- * effect. So the listener of `mouseover` and `mouseout` should
- * check `isInEmphasis`.
- *
- * @param {module:zrender/src/Element} el element that will take the effect.
- * @param {Function} onEmphasis
- * @param {Function} onNormal
- */
-export function setExtraHighDownEffect(el, onEmphasis, onNormal) {
-    el.__extraOnEmphasis = onEmphasis;
-    el.__extraOnNormal = onNormal;
-}
-
-/**
- * @param {module:zrender/src/Element} el element that will take the effect.
- */
-export function removeExtraHighDownEffect(el) {
-    el.__extraOnEmphasis = el.__extraOnNormal = null;
-}
-
 function onElementMouseOver(e) {
     !shouldSilent(this, e)
         // API highlight has higher priority than mouse highlight.
         && !this.__emphasisEnteredByAPI
-        && traverseCall(this, singleEnterEmphasis);
+        && traverseUpdate(this, singleEnterEmphasis);
 }
 
 function onElementMouseOut(e) {
     !shouldSilent(this, e)
         // API highlight has higher priority than mouse highlight.
         && !this.__emphasisEnteredByAPI
-        && traverseCall(this, singleEnterNormal);
-}
-
-function shouldSilent(el, e) {
-    return el.__highDownSilentOnTouch && e.zrByTouch;
+        && traverseUpdate(this, singleEnterNormal);
 }
 
 function onEnterEmphasisByAPI() {
     this.__emphasisEnteredByAPI = true;
-    traverseCall(this, singleEnterEmphasis);
+    traverseUpdate(this, singleEnterEmphasis);
 }
 
 function onEnterNormalByAPI() {
     this.__emphasisEnteredByAPI = false;
-    traverseCall(this, singleEnterNormal);
+    traverseUpdate(this, singleEnterNormal);
+}
+
+function shouldSilent(el, e) {
+    return el.__highDownSilentOnTouch && e.zrByTouch;
 }
 
 /**
@@ -504,43 +487,48 @@ function onEnterNormalByAPI() {
  * (3) These input parameters can be set directly on `el`:
  *
  * @param {module:zrender/Element} el
- * @param {Object} [el.hoverStyle] Can be set on el or its descendants,
- *                 e.g., `el.hoverStyle = ...; graphic.setHoverStyle(el); `.
+ * @param {Object} [el.hoverStyle] See `graphic.setElementHoverStyle`.
  * @param {boolean} [el.highDownSilentOnTouch=false] See `graphic.setAsHighDownDispatcher`.
- * @param {Function} [el.highDownTransition] See `graphic.setElementHoverStyle`.
+ * @param {Function} [el.highDownOnUpdate] See `graphic.setAsHighDownDispatcher`.
  * @param {Object|boolean} [hoverStyle] See `graphic.setElementHoverStyle`.
  */
 export function setHoverStyle(el, hoverStyle) {
-    el.isGroup
-        ? el.traverse(function (child) {
-            // If element has sepcified hoverStyle, then use it instead of given hoverStyle
-            // Often used when item group has a label element and it's hoverStyle is different
-            !child.isGroup && setElementHoverStyle(child, child.hoverStyle || hoverStyle);
-        })
-        : setElementHoverStyle(el, el.hoverStyle || hoverStyle);
-
     setAsHighDownDispatcher(el, true);
+    traverseUpdate(el, setElementHoverStyle, hoverStyle);
 }
 
 /**
  * @param {module:zrender/Element} el
+ * @param {Function} [el.highDownOnUpdate] Called when state updated.
+ *        Since `setHoverStyle` has the constraint that it must be called after
+ *        all of the normal style updated, `highDownOnUpdate` is not needed to
+ *        trigger if both `fromState` and `toState` is 'normal', and needed to
+ *        trigger if both `fromState` and `toState` is 'emphasis', which enables
+ *        to sync outside style settings to "emphasis" state.
+ *        @this {string} This dispatcher `el`.
+ *        @param {string} fromState Can be "normal" or "emphasis".
+ *               `fromState` might equal to `toState`,
+ *               for example, when this method is called when `el` is
+ *               on "emphasis" state.
+ *        @param {string} toState Can be "normal" or "emphasis".
  * @param {boolean} [el.highDownSilentOnTouch=false]
- *      In touch device, mouseover event will be trigger on touchstart event
- *      (see module:zrender/dom/HandlerProxy). By this mechanism, we can
- *      conveniently use hoverStyle when tap on touch screen without additional
- *      code for compatibility.
- *      But if the chart/component has select feature, which usually also use
- *      hoverStyle, there might be conflict between 'select-highlight' and
- *      'hover-highlight' especially when roam is enabled (see geo for example).
- *      In this case, `highDownSilentOnTouch` should be used to disable
- *      hover-highlight on touch device.
+ *        In touch device, mouseover event will be trigger on touchstart event
+ *        (see module:zrender/dom/HandlerProxy). By this mechanism, we can
+ *        conveniently use hoverStyle when tap on touch screen without additional
+ *        code for compatibility.
+ *        But if the chart/component has select feature, which usually also use
+ *        hoverStyle, there might be conflict between 'select-highlight' and
+ *        'hover-highlight' especially when roam is enabled (see geo for example).
+ *        In this case, `highDownSilentOnTouch` should be used to disable
+ *        hover-highlight on touch device.
  * @param {boolean} [asDispatcher=true] If `false`, do not set as "highDownDispatcher".
  */
 export function setAsHighDownDispatcher(el, asDispatcher) {
     var disable = asDispatcher === false;
-    // Make `highDownSilentOnTouch` only work after `setAsHighDownDispatcher`
-    // called. Avoid it is modified by user unexpectedly.
+    // Make `highDownSilentOnTouch` and `highDownOnUpdate` only work after
+    // `setAsHighDownDispatcher` called. Avoid it is modified by user unexpectedly.
     el.__highDownSilentOnTouch = el.highDownSilentOnTouch;
+    el.__highDownOnUpdate = el.highDownOnUpdate;
 
     // Simple optimize, since this method might be
     // called for each elements of a group in some cases.
diff --git a/test/hoverStyle.html b/test/hoverStyle.html
index 40a656c..e10c023 100644
--- a/test/hoverStyle.html
+++ b/test/hoverStyle.html
@@ -133,7 +133,6 @@ under the License.
 
                 return chart;
             }
-
         </script>
 
 
@@ -142,6 +141,14 @@ under the License.
 
 
 
+
+
+
+
+
+
+
+
         <script>
             require(['echarts'], function (echarts) {
                 var option = {
@@ -160,8 +167,8 @@ under the License.
                         right: 10,
                         splitNumber: 3,
                         type: 'piecewise',
-                        // demension: 1,
-                        // hoverLink: true,
+                        demension: 1,
+                        hoverLink: true,
                         inRange: {
                             color: ['blue', 'red']
                         },
@@ -173,20 +180,70 @@ under the License.
                         type: 'scatter',
                         symbolSize: 30,
                         data: [
-                            [12, 331],
-                            [55, 131],
-                            [55, 531]
+                            [12, 331], [55, 131], [55, 531]
+                        ]
+                    }, {
+                        type: 'scatter',
+                        symbol: 'rect',
+                        symbolSize: 40,
+                        label: {
+                            show: true,
+                            formatter: 'label inside\nhover red\n{c}'
+                        },
+                        emphasis: {
+                            label: {
+                                color: 'red'
+                            }
+                        },
+                        data: [
+                            [112, 331], [115, 131], [165, 531]
+                        ]
+                    }, {
+                        type: 'scatter',
+                        symbol: 'triangle',
+                        symbolSize: 40,
+                        label: {
+                            show: true,
+                            formatter: 'label inside\n{c}'
+                        },
+                        emphasis: {
+                            label: {
+                                position: 'top',
+                                formatter: 'hover become top\n{c}'
+                            }
+                        },
+                        data: [
+                            [212, 331], [215, 131], [265, 531]
                         ]
                     }]
                 };
 
-                testHelper.create(echarts, 'mainb1', {
+                var chart = testHelper.create(echarts, 'mainb1', {
                     title: [
-                        'visualMap should be normal (and hoverLink should be normal)',
-                        '(inRange: has color, outOfRange: "black")'
+                        'visualMap should be normal (and hoverLink should **scale circles**)',
+                        '(inRange: has color, outOfRange: "black")',
+                        'Click visualMap when hoverlink, circle color should be changed, but **keep scaled**.',
+                        'Click the buttons check correct.'
                     ],
                     option: option,
-                    height: 200
+                    height: 300,
+                    buttons: [{
+                        text: 'highlight dataIndex 0',
+                        onclick: function () {
+                            chart.dispatchAction({
+                                type: 'highlight',
+                                dataIndex: 0
+                            });
+                        }
+                    }, {
+                        text: 'downplay dataIndex 0',
+                        onclick: function () {
+                            chart.dispatchAction({
+                                type: 'downplay',
+                                dataIndex: 0
+                            });
+                        }
+                    }]
                 });
             });
         </script>
@@ -643,9 +700,9 @@ under the License.
                     series: [{
                         type: 'pie',
                         data: [
-                            {value: 12, name: 'qwer'},
-                            {value: 15, name: 'asdf'},
-                            {value: 17, name: 'zxcv'}
+                            {value: 12, name: 'Click this sector:\nit keep color-lifted and scaled'},
+                            {value: 15, name: 'zxcv'},
+                            {value: 17, name: 'Click this sector:\nit becomes yellow, keep color-lifted and scaled'}
                         ]
                     }]
                 };
@@ -653,9 +710,11 @@ under the License.
                 var chart = testHelper.create(echarts, 'main6', {
                     title: [
                         'Pie **hightlight priority** of API and mouse:',
-                        'Hover on sector, **should scaled and highlighted**.',
-                        'trigger hover by API: **should scaled and highlighted**.',
+                        'Hover on sector, **should scaled and color lifted**.',
+                        'trigger hover by API: **should scaled and color-lifted**.',
                         'Test mouse hover and leave, should NOT return to normal.',
+                        'Only click downplay to return normal',
+                        'Click the red sector, it **keep scaled and color-lifted**',
                         'Only click downplay to return normal'
                     ],
                     option: option,
@@ -680,6 +739,20 @@ under the License.
                         }
                     }]
                 });
+
+                chart && chart.on('click', function (e) {
+                    if (e.dataIndex === 0) {
+                        chart.dispatchAction({
+                            type: 'highlight',
+                            seriesIndex: 0,
+                            dataIndex: 0
+                        });
+                    }
+                    else if (e.dataIndex === 2) {
+                        option.series[0].data[2].itemStyle = {color: 'yellow'};
+                        chart.setOption(option);
+                    }
+                });
             });
         </script>
 


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


[incubator-echarts] 02/02: Fix highlight conflict.

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sushuang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-echarts.git

commit 2b5a3ee4b674f1dd8b9be16b65bf530e65eb7da4
Author: sushuang <su...@gmail.com>
AuthorDate: Fri Jan 4 04:44:32 2019 +0800

    Fix highlight conflict.
---
 src/component/visualMap/ContinuousView.js |  6 ++---
 src/component/visualMap/PiecewiseModel.js |  2 +-
 src/component/visualMap/PiecewiseView.js  |  5 ++--
 src/component/visualMap/helper.js         |  9 ++++---
 src/util/graphic.js                       | 44 +++++++++++++++++++++++--------
 src/view/Chart.js                         | 25 +++++++++++-------
 test/hoverStyle.html                      |  3 ++-
 7 files changed, 62 insertions(+), 32 deletions(-)

diff --git a/src/component/visualMap/ContinuousView.js b/src/component/visualMap/ContinuousView.js
index b72e407..a765203 100644
--- a/src/component/visualMap/ContinuousView.js
+++ b/src/component/visualMap/ContinuousView.js
@@ -710,8 +710,8 @@ var ContinuousView = VisualMapView.extend({
 
         var resultBatches = modelUtil.compressBatches(oldBatch, newBatch);
 
-        this._dispatchHighDown('downplay', helper.convertDataIndex(resultBatches[0]));
-        this._dispatchHighDown('highlight', helper.convertDataIndex(resultBatches[1]));
+        this._dispatchHighDown('downplay', helper.makeHighDownBatch(resultBatches[0], visualMapModel));
+        this._dispatchHighDown('highlight', helper.makeHighDownBatch(resultBatches[1], visualMapModel));
     },
 
     /**
@@ -755,7 +755,7 @@ var ContinuousView = VisualMapView.extend({
         this._hideIndicator();
 
         var indices = this._hoverLinkDataIndices;
-        this._dispatchHighDown('downplay', helper.convertDataIndex(indices));
+        this._dispatchHighDown('downplay', helper.makeHighDownBatch(indices, this.visualMapModel));
 
         indices.length = 0;
     },
diff --git a/src/component/visualMap/PiecewiseModel.js b/src/component/visualMap/PiecewiseModel.js
index d3c1565..df03ed0 100644
--- a/src/component/visualMap/PiecewiseModel.js
+++ b/src/component/visualMap/PiecewiseModel.js
@@ -271,7 +271,7 @@ var PiecewiseModel = VisualMapModel.extend({
     /**
      * @public
      * @params {number} pieceIndex piece index in visualMapModel.getPieceList()
-     * @return {Array.<Object>} [{seriesId, dataIndices: <Array.<number>>}, ...]
+     * @return {Array.<Object>} [{seriesId, dataIndex: <Array.<number>>}, ...]
      */
     findTargetDataIndices: function (pieceIndex) {
         var result = [];
diff --git a/src/component/visualMap/PiecewiseView.js b/src/component/visualMap/PiecewiseView.js
index 885f2d7..eceb66f 100644
--- a/src/component/visualMap/PiecewiseView.js
+++ b/src/component/visualMap/PiecewiseView.js
@@ -114,8 +114,9 @@ var PiecewiseVisualMapView = VisualMapView.extend({
 
             visualMapModel.option.hoverLink && this.api.dispatchAction({
                 type: method,
-                batch: helper.convertDataIndex(
-                    visualMapModel.findTargetDataIndices(pieceIndex)
+                batch: helper.makeHighDownBatch(
+                    visualMapModel.findTargetDataIndices(pieceIndex),
+                    visualMapModel
                 )
             });
         }
diff --git a/src/component/visualMap/helper.js b/src/component/visualMap/helper.js
index afe1890..4f35a11 100644
--- a/src/component/visualMap/helper.js
+++ b/src/component/visualMap/helper.js
@@ -64,12 +64,13 @@ export function getItemAlign(visualMapModel, api, itemSize) {
  * Prepare dataIndex for outside usage, where dataIndex means rawIndex, and
  * dataIndexInside means filtered index.
  */
-export function convertDataIndex(batch) {
+export function makeHighDownBatch(batch, visualMapModel) {
     zrUtil.each(batch || [], function (batchItem) {
-        if (batch.dataIndex != null) {
-            batch.dataIndexInside = batch.dataIndex;
-            batch.dataIndex = null;
+        if (batchItem.dataIndex != null) {
+            batchItem.dataIndexInside = batchItem.dataIndex;
+            batchItem.dataIndex = null;
         }
+        batchItem.highlightKey = 'visualMap' + (visualMapModel ? visualMapModel.componentIndex : '');
     });
     return batch;
 }
diff --git a/src/util/graphic.js b/src/util/graphic.js
index 4e3e583..69c8e37 100644
--- a/src/util/graphic.js
+++ b/src/util/graphic.js
@@ -53,6 +53,10 @@ var Z2_LIFT_VALUE = 1;
 var EMPHASIS = 'emphasis';
 var NORMAL = 'normal';
 
+// Reserve 0 as default.
+var _highlightNextDigit = 1;
+var _highlightKeyMap = {};
+
 
 /**
  * Extend shape with parameters
@@ -429,26 +433,26 @@ export function setElementHoverStyle(el, hoverStl) {
 
 function onElementMouseOver(e) {
     !shouldSilent(this, e)
-        // API highlight has higher priority than mouse highlight.
-        && !this.__emphasisEnteredByAPI
+        // "emphasis" event highlight has higher priority than mouse highlight.
+        && !this.__highByOuter
         && traverseUpdate(this, singleEnterEmphasis);
 }
 
 function onElementMouseOut(e) {
     !shouldSilent(this, e)
-        // API highlight has higher priority than mouse highlight.
-        && !this.__emphasisEnteredByAPI
+        // "emphasis" event highlight has higher priority than mouse highlight.
+        && !this.__highByOuter
         && traverseUpdate(this, singleEnterNormal);
 }
 
-function onEnterEmphasisByAPI() {
-    this.__emphasisEnteredByAPI = true;
+function onElementEmphasisEvent(highlightDigit) {
+    this.__highByOuter |= 1 << (highlightDigit || 0);
     traverseUpdate(this, singleEnterEmphasis);
 }
 
-function onEnterNormalByAPI() {
-    this.__emphasisEnteredByAPI = false;
-    traverseUpdate(this, singleEnterNormal);
+function onElementNormalEvent(highlightDigit) {
+    !(this.__highByOuter &= ~(1 << (highlightDigit || 0)))
+        && traverseUpdate(this, singleEnterNormal);
 }
 
 function shouldSilent(el, e) {
@@ -537,8 +541,10 @@ export function setAsHighDownDispatcher(el, asDispatcher) {
 
         // Duplicated function will be auto-ignored, see Eventful.js.
         el[method]('mouseover', onElementMouseOver)[method]('mouseout', onElementMouseOut);
-        // Emphasis, normal can be triggered manually
-        el[method]('emphasis', onEnterEmphasisByAPI)[method]('normal', onEnterNormalByAPI);
+        // Emphasis, normal can be triggered manually by API or other components like hover link.
+        el[method]('emphasis', onElementEmphasisEvent)[method]('normal', onElementNormalEvent);
+        // Also keep previous record.
+        el.__highByOuter = el.__highByOuter || 0;
 
         el.__highDownDispatcher = !disable;
     }
@@ -553,6 +559,22 @@ export function isHighDownDispatcher(el) {
 }
 
 /**
+ * Support hightlight/downplay record on each elements.
+ * For the case: hover highlight/downplay (legend, visualMap, ...) and
+ * user triggerred hightlight/downplay should not conflict.
+ * Only all of the highlightDigit cleared, return to normal.
+ * @param {string} highlightKey
+ * @return {number} highlightDigit
+ */
+export function getHighlightDigit(highlightKey) {
+    var highlightDigit = _highlightKeyMap[highlightKey];
+    if (highlightDigit == null && _highlightNextDigit <= 32) {
+        highlightDigit = _highlightKeyMap[highlightKey] = _highlightNextDigit++;
+    }
+    return highlightDigit;
+}
+
+/**
  * See more info in `setTextStyleCommon`.
  * @param {Object|module:zrender/graphic/Style} normalStyle
  * @param {Object} emphasisStyle
diff --git a/src/view/Chart.js b/src/view/Chart.js
index 7c71b01..bc7c70b 100644
--- a/src/view/Chart.js
+++ b/src/view/Chart.js
@@ -157,27 +157,28 @@ Chart.prototype = {
 };
 
 var chartProto = Chart.prototype;
-chartProto.updateView
-    = chartProto.updateLayout
-    = chartProto.updateVisual
-    = function (seriesModel, ecModel, api, payload) {
+chartProto.updateView =
+chartProto.updateLayout =
+chartProto.updateVisual =
+    function (seriesModel, ecModel, api, payload) {
         this.render(seriesModel, ecModel, api, payload);
     };
 
 /**
  * Set state of single element
  * @param {module:zrender/Element} el
- * @param  {string} state 'normal'|'emphasis'
+ * @param {string} state 'normal'|'emphasis'
+ * @param {number} highlightDigit
  */
-function elSetState(el, state) {
+function elSetState(el, state, highlightDigit) {
     if (el) {
-        el.trigger(state);
+        el.trigger(state, highlightDigit);
         if (el.isGroup
             // Simple optimize.
             && !graphicUtil.isHighDownDispatcher(el)
         ) {
             for (var i = 0, len = el.childCount(); i < len; i++) {
-                elSetState(el.childAt(i), state);
+                elSetState(el.childAt(i), state, highlightDigit);
             }
         }
     }
@@ -191,14 +192,18 @@ function elSetState(el, state) {
 function toggleHighlight(data, payload, state) {
     var dataIndex = modelUtil.queryDataIndex(data, payload);
 
+    var highlightDigit = (payload && payload.highlightKey != null)
+        ? graphicUtil.getHighlightDigit(payload.highlightKey)
+        : null;
+
     if (dataIndex != null) {
         each(modelUtil.normalizeToArray(dataIndex), function (dataIdx) {
-            elSetState(data.getItemGraphicEl(dataIdx), state);
+            elSetState(data.getItemGraphicEl(dataIdx), state, highlightDigit);
         });
     }
     else {
         data.eachItemGraphicEl(function (el) {
-            elSetState(el, state);
+            elSetState(el, state, highlightDigit);
         });
     }
 }
diff --git a/test/hoverStyle.html b/test/hoverStyle.html
index e10c023..d8943cf 100644
--- a/test/hoverStyle.html
+++ b/test/hoverStyle.html
@@ -223,7 +223,8 @@ under the License.
                         'visualMap should be normal (and hoverLink should **scale circles**)',
                         '(inRange: has color, outOfRange: "black")',
                         'Click visualMap when hoverlink, circle color should be changed, but **keep scaled**.',
-                        'Click the buttons check correct.'
+                        'Click button highlight dataIndex 0, only "middle blue" highlighted',
+                        'Then hover **blue** visualMap, all blue highlighted, and then unhover, "middle blue" should keep highlighted'
                     ],
                     option: option,
                     height: 300,


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