You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@echarts.apache.org by wa...@apache.org on 2021/02/08 08:46:24 UTC

[echarts] branch perf-tooltip created (now a04213d)

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

wangzx pushed a change to branch perf-tooltip
in repository https://gitbox.apache.org/repos/asf/echarts.git.


      at a04213d  perf(tooltip): optimize the performance of tooltip.

This branch includes the following new commits:

     new a04213d  perf(tooltip): optimize the performance of tooltip.

The 1 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.



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


[echarts] 01/01: perf(tooltip): optimize the performance of tooltip.

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

wangzx pushed a commit to branch perf-tooltip
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit a04213d58785e9e60f63bc8b3090fa33439b177f
Author: plainheart <yh...@all-my-life.cn>
AuthorDate: Mon Feb 8 16:42:37 2021 +0800

    perf(tooltip): optimize the performance of tooltip.
---
 src/component/tooltip/TooltipHTMLContent.ts | 122 +++++++++++++++++-----------
 src/component/tooltip/TooltipView.ts        |   7 +-
 src/component/tooltip/helper.ts             |  29 +++++++
 3 files changed, 106 insertions(+), 52 deletions(-)

diff --git a/src/component/tooltip/TooltipHTMLContent.ts b/src/component/tooltip/TooltipHTMLContent.ts
index 8f60093..d096da8 100644
--- a/src/component/tooltip/TooltipHTMLContent.ts
+++ b/src/component/tooltip/TooltipHTMLContent.ts
@@ -17,7 +17,7 @@
 * under the License.
 */
 
-import { isString, indexOf, map, each, bind, isArray, isDom } from 'zrender/src/core/util';
+import { isString, indexOf, each, bind, isArray, isDom } from 'zrender/src/core/util';
 import { toHex } from 'zrender/src/tool/color';
 import { normalizeEvent } from 'zrender/src/core/event';
 import { transformLocalCoord } from 'zrender/src/core/dom';
@@ -31,12 +31,16 @@ import { ZRRawEvent } from 'zrender/src/core/types';
 import { ColorString, ZRColor } from '../../util/types';
 import CanvasPainter from 'zrender/src/canvas/Painter';
 import SVGPainter from 'zrender/src/svg/Painter';
-import { shouldTooltipConfine } from './helper';
+import { shouldTooltipConfine, toCSSVendorPrefix, TRANSFORM_VENDOR, TRANSITION_VENDOR } from './helper';
 import { getPaddingFromTooltipModel } from './tooltipMarkup';
 
-const vendors = ['-ms-', '-moz-', '-o-', '-webkit-', ''];
+/* global document, window */
 
-const gCssText = 'position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;';
+const CSS_TRANSITION_VENDOR = toCSSVendorPrefix(TRANSITION_VENDOR, 'transition');
+const CSS_TRANSFORM_VENDOR = toCSSVendorPrefix(TRANSFORM_VENDOR, 'transform');
+
+// eslint-disable-next-line
+const gCssText = `position:absolute;display:block;border-style:solid;white-space:nowrap;z-index:9999999;${env.transform3dSupported ? 'will-change:transform;' : ''}`;
 
 function mirrorPos(pos: string): string {
     pos = pos === 'left'
@@ -60,24 +64,20 @@ function assembleArrow(
 
     borderColor = convertToColorString(borderColor);
     const arrowPos = mirrorPos(arrowPosition);
-    let positionStyle = '';
-    let transformStyle = '';
+    let positionStyle = `${arrowPos}:-6px;`;
+    let transformStyle = CSS_TRANSFORM_VENDOR + ':';
     if (indexOf(['left', 'right'], arrowPos) > -1) {
-        positionStyle = `${arrowPos}:-6px;top:50%;`;
-        transformStyle = `translateY(-50%) rotate(${arrowPos === 'left' ? -225 : -45}deg)`;
+        positionStyle += 'top:50%';
+        transformStyle += `translateY(-50%) rotate(${arrowPos === 'left' ? -225 : -45}deg)`;
     }
     else {
-        positionStyle = `${arrowPos}:-6px;left:50%;`;
-        transformStyle = `translateX(-50%) rotate(${arrowPos === 'top' ? 225 : 45}deg)`;
+        positionStyle += 'left:50%';
+        transformStyle += `translateX(-50%) rotate(${arrowPos === 'top' ? 225 : 45}deg)`;
     }
 
-    transformStyle = map(vendors, function (vendorPrefix) {
-        return vendorPrefix + 'transform:' + transformStyle;
-    }).join(';');
-
     const styleCss = [
         'position:absolute;width:10px;height:10px;',
-        `${positionStyle}${transformStyle};`,
+        `${positionStyle};${transformStyle};`,
         `border-bottom: ${borderColor} solid 1px;`,
         `border-right: ${borderColor} solid 1px;`,
         `background-color: ${backgroundColor};`,
@@ -88,16 +88,36 @@ function assembleArrow(
 
 function assembleTransition(duration: number, onlyFade?: boolean): string {
     const transitionCurve = 'cubic-bezier(0.23, 1, 0.32, 1)';
-    let transitionText = 'opacity ' + (duration / 2) + 's ' + transitionCurve + ','
-                       + 'visibility ' + (duration / 2) + 's ' + transitionCurve;
+    let transitionText = 'opacity ' + (duration / 2) + 's ' + transitionCurve
+                       + ',visibility ' + (duration / 2) + 's ' + transitionCurve;
     if (!onlyFade) {
-        transitionText += ',left ' + duration + 's ' + transitionCurve
-                        + ',top ' + duration + 's ' + transitionCurve;
+        transitionText += env.transform3dSupported
+            ? `,${TRANSFORM_VENDOR} ${duration}s ${transitionCurve}`
+            : `,left ${duration}s ${transitionCurve},top ${duration}s ${transitionCurve}`;
     }
 
-    return map(vendors, function (vendorPrefix) {
-        return vendorPrefix + 'transition:' + transitionText;
-    }).join(';');
+    return CSS_TRANSITION_VENDOR + ':' + transitionText;
+}
+
+function assembleTransform(el: HTMLElement, x: number, y: number, zrHeight: number, toString?: boolean) {
+    // If using float on style, the final width of the dom might
+    // keep changing slightly while mouse move. So `toFixed(0)` them.
+    const x0 = x.toFixed(0);
+    let y0;
+    // not support transform, use `left` and `top` instead.
+    if (!env.transform3dSupported) {
+        y0 = (y - el.offsetHeight / 2).toFixed(0);
+        return toString
+            ? `top:${y0}px;left:${x0}px;`
+            : [['top', `${y0}px`], ['left', `${x0}px`]];
+    }
+    // support transform
+    y0 = (y - zrHeight).toFixed(0);
+    const ie3d = env.ieTransform3dSupported;
+    const translate = `translate${ie3d ? '' : '3d'}(${x0}px,${y0}px${ie3d ? '' : ',0'})`;
+    return toString
+        ? CSS_TRANSFORM_VENDOR + ':' + translate + ';'
+        : [[TRANSFORM_VENDOR, translate]];
 }
 
 /**
@@ -153,12 +173,12 @@ function assembleCssText(tooltipModel: Model<TooltipOption>, enableTransition?:
 
     if (backgroundColor) {
         if (env.canvasSupported) {
-            cssText.push('background-Color:' + backgroundColor);
+            cssText.push('background-color:' + backgroundColor);
         }
         else {
             // for ie
             cssText.push(
-                'background-Color:#' + toHex(backgroundColor)
+                'background-color:#' + toHex(backgroundColor)
             );
             cssText.push('filter:alpha(opacity=70)');
         }
@@ -347,25 +367,23 @@ class TooltipHTMLContent {
         clearTimeout(this._hideTimeout);
         clearTimeout(this._longHideTimeout);
         const el = this.el;
-        const styleCoord = this._styleCoord;
-        const offset = el.offsetHeight / 2;
-        nearPointColor = convertToColorString(nearPointColor);
-        el.style.cssText = gCssText + assembleCssText(tooltipModel, !this._firstShow, this._longHide)
-            // Because of the reason described in:
-            // http://stackoverflow.com/questions/21125587/css3-transition-not-working-in-chrome-anymore
-            // we should set initial value to `left` and `top`.
-            + ';left:' + styleCoord[0] + 'px;top:' + (styleCoord[1] - offset) + 'px;'
-            + `border-color: ${nearPointColor};`
-            + (tooltipModel.get('extraCssText') || '');
-
-        el.style.display = el.innerHTML ? 'block' : 'none';
-
-        // If mouse occasionally move over the tooltip, a mouseout event will be
-        // triggered by canvas, and cause some unexpectable result like dragging
-        // stop, "unfocusAdjacency". Here `pointer-events: none` is used to solve
-        // it. Although it is not supported by IE8~IE10, fortunately it is a rare
-        // scenario.
-        el.style.pointerEvents = this._enterable ? 'auto' : 'none';
+        const style = el.style;
+        if (!el.innerHTML) {
+            style.display = 'none';
+        }
+        else {
+            style.cssText = gCssText
+                + assembleCssText(tooltipModel, !this._firstShow, this._longHide)
+                + `${TRANSFORM_VENDOR}:${style[TRANSFORM_VENDOR as any]};`
+                + `border-color:${convertToColorString(nearPointColor)};`
+                + (tooltipModel.get('extraCssText') || '')
+                // If mouse occasionally move over the tooltip, a mouseout event will be
+                // triggered by canvas, and cause some unexpectable result like dragging
+                // stop, "unfocusAdjacency". Here `pointer-events: none` is used to solve
+                // it. Although it is not supported by IE8~IE10, fortunately it is a rare
+                // scenario.
+                + `pointer-event:${this._enterable ? 'auto' : 'none'}`;
+        }
 
         this._show = true;
         this._firstShow = false;
@@ -421,10 +439,14 @@ class TooltipHTMLContent {
 
         if (styleCoord[0] != null && styleCoord[1] != null) {
             const style = this.el.style;
-            // If using float on style, the final width of the dom might
-            // keep changing slightly while mouse move. So `toFixed(0)` them.
-            style.left = styleCoord[0].toFixed(0) + 'px';
-            style.top = styleCoord[1].toFixed(0) + 'px';
+            const transforms = assembleTransform(
+                this.el,
+                styleCoord[0], styleCoord[1],
+                this._zr.getHeight()
+            ) as string[][];
+            each(transforms, (transform) => {
+              style[transform[0] as any] = transform[1];
+            });
         }
     }
 
@@ -444,8 +466,10 @@ class TooltipHTMLContent {
     }
 
     hide() {
-        this.el.style.visibility = 'hidden';
-        this.el.style.opacity = '0';
+        const style = this.el.style;
+        style.visibility = 'hidden';
+        style.opacity = '0';
+        env.transform3dSupported && (style.willChange = '');
         this._show = false;
         this._longHideTimeout = setTimeout(() => this._longHide = true, 500) as any;
     }
diff --git a/src/component/tooltip/TooltipView.ts b/src/component/tooltip/TooltipView.ts
index cd52e2b..6a6da1e 100644
--- a/src/component/tooltip/TooltipView.ts
+++ b/src/component/tooltip/TooltipView.ts
@@ -718,6 +718,7 @@ class TooltipView extends ComponentView {
             tooltipModel.get('trigger'),
             tooltipModel.get('borderColor')
         );
+        const nearPointColor = nearPoint.color;
 
         if (formatter && zrUtil.isString(formatter)) {
             const useUTC = tooltipModel.ecModel.get('useUTC');
@@ -732,7 +733,7 @@ class TooltipView extends ComponentView {
         else if (zrUtil.isFunction(formatter)) {
             const callback = bind(function (cbTicket: string, html: string | HTMLElement[]) {
                 if (cbTicket === this._ticket) {
-                    tooltipContent.setContent(html, markupStyleCreator, tooltipModel, nearPoint.color, positionExpr);
+                    tooltipContent.setContent(html, markupStyleCreator, tooltipModel, nearPointColor, positionExpr);
                     this._updatePosition(
                         tooltipModel, positionExpr, x, y, tooltipContent, params, el
                     );
@@ -742,8 +743,8 @@ class TooltipView extends ComponentView {
             html = formatter(params, asyncTicket, callback);
         }
 
-        tooltipContent.setContent(html, markupStyleCreator, tooltipModel, nearPoint.color, positionExpr);
-        tooltipContent.show(tooltipModel, nearPoint.color);
+        tooltipContent.setContent(html, markupStyleCreator, tooltipModel, nearPointColor, positionExpr);
+        tooltipContent.show(tooltipModel, nearPointColor);
         this._updatePosition(
             tooltipModel, positionExpr, x, y, tooltipContent, params, el
         );
diff --git a/src/component/tooltip/helper.ts b/src/component/tooltip/helper.ts
index 3520056..feaf30c 100644
--- a/src/component/tooltip/helper.ts
+++ b/src/component/tooltip/helper.ts
@@ -19,6 +19,9 @@
 
 import { TooltipOption } from './TooltipModel';
 import Model from '../../model/Model';
+import { toCamelCase } from '../../util/format';
+
+/* global document */
 
 export function shouldTooltipConfine(tooltipModel: Model<TooltipOption>): boolean {
     const confineOption = tooltipModel.get('confine');
@@ -27,3 +30,29 @@ export function shouldTooltipConfine(tooltipModel: Model<TooltipOption>): boolea
         // In richText mode, the outside part can not be visible.
         : tooltipModel.get('renderMode') === 'richText';
 }
+
+function testStyle(styleProps: string[]) {
+	const style = document.documentElement.style;
+	for (let i = 0, len = styleProps.length; i < len; i++) {
+		if (styleProps[i] in style) {
+			return styleProps[i];
+		}
+	}
+}
+
+export const TRANSFORM_VENDOR = testStyle(
+	['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform']
+);
+
+export const TRANSITION_VENDOR = testStyle(
+	['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']
+);
+
+export function toCSSVendorPrefix(styleVendor: string, styleProp: string) {
+	styleProp = toCamelCase(styleProp, true);
+	const idx = styleVendor.indexOf(styleProp);
+	styleVendor = idx === -1
+		? styleProp
+		: `-${styleVendor.slice(0, idx)}-${styleProp}`;
+	return styleVendor.toLowerCase();
+}


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