From a04213d58785e9e60f63bc8b3090fa33439b177f Mon Sep 17 00:00:00 2001 From: plainheart Date: Mon, 8 Feb 2021 16:42:37 +0800 Subject: [PATCH 1/8] 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 8f600934dc..d096da84c0 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, 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 cd52e2b1e5..6a6da1e74e 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 352005662a..feaf30cfa0 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): boolean { const confineOption = tooltipModel.get('confine'); @@ -27,3 +30,29 @@ export function shouldTooltipConfine(tooltipModel: Model): 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(); +} From 7fe6d4d3c5ec62a8369385f2e8345d8b02749e3f Mon Sep 17 00:00:00 2001 From: plainheart Date: Thu, 25 Feb 2021 17:12:01 +0800 Subject: [PATCH 2/8] fix(tooltip): rename `ieTransform3dSupported` to be `transformSupported`. --- src/component/tooltip/TooltipHTMLContent.ts | 10 +++++----- src/component/tooltip/helper.ts | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/component/tooltip/TooltipHTMLContent.ts b/src/component/tooltip/TooltipHTMLContent.ts index d096da84c0..07651b1995 100644 --- a/src/component/tooltip/TooltipHTMLContent.ts +++ b/src/component/tooltip/TooltipHTMLContent.ts @@ -91,7 +91,7 @@ function assembleTransition(duration: number, onlyFade?: boolean): string { let transitionText = 'opacity ' + (duration / 2) + 's ' + transitionCurve + ',visibility ' + (duration / 2) + 's ' + transitionCurve; if (!onlyFade) { - transitionText += env.transform3dSupported + transitionText += env.transformSupported ? `,${TRANSFORM_VENDOR} ${duration}s ${transitionCurve}` : `,left ${duration}s ${transitionCurve},top ${duration}s ${transitionCurve}`; } @@ -105,7 +105,7 @@ function assembleTransform(el: HTMLElement, x: number, y: number, zrHeight: numb const x0 = x.toFixed(0); let y0; // not support transform, use `left` and `top` instead. - if (!env.transform3dSupported) { + if (!env.transformSupported) { y0 = (y - el.offsetHeight / 2).toFixed(0); return toString ? `top:${y0}px;left:${x0}px;` @@ -113,8 +113,8 @@ function assembleTransform(el: HTMLElement, x: number, y: number, zrHeight: numb } // support transform y0 = (y - zrHeight).toFixed(0); - const ie3d = env.ieTransform3dSupported; - const translate = `translate${ie3d ? '' : '3d'}(${x0}px,${y0}px${ie3d ? '' : ',0'})`; + const is3d = env.transform3dSupported; + const translate = `translate${is3d ? '3d' : ''}(${x0}px,${y0}px${is3d ? ',0' : ''})`; return toString ? CSS_TRANSFORM_VENDOR + ':' + translate + ';' : [[TRANSFORM_VENDOR, translate]]; @@ -382,7 +382,7 @@ class TooltipHTMLContent { // 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'}`; + + `;pointer-event:${this._enterable ? 'auto' : 'none'}`; } this._show = true; diff --git a/src/component/tooltip/helper.ts b/src/component/tooltip/helper.ts index feaf30cfa0..3b6d70629b 100644 --- a/src/component/tooltip/helper.ts +++ b/src/component/tooltip/helper.ts @@ -31,7 +31,7 @@ export function shouldTooltipConfine(tooltipModel: Model): boolea : tooltipModel.get('renderMode') === 'richText'; } -function testStyle(styleProps: string[]) { +function testStyle(styleProps: string[]): string | undefined { const style = document.documentElement.style; for (let i = 0, len = styleProps.length; i < len; i++) { if (styleProps[i] in style) { From ce4d2375d56044aa033afa56bb44df711129a467 Mon Sep 17 00:00:00 2001 From: plainheart Date: Sat, 27 Feb 2021 18:29:28 +0800 Subject: [PATCH 3/8] perf(tooltip): reduce strings. --- src/component/tooltip/TooltipHTMLContent.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/component/tooltip/TooltipHTMLContent.ts b/src/component/tooltip/TooltipHTMLContent.ts index 07651b1995..9c1a27fbcf 100644 --- a/src/component/tooltip/TooltipHTMLContent.ts +++ b/src/component/tooltip/TooltipHTMLContent.ts @@ -75,25 +75,27 @@ function assembleArrow( transformStyle += `translateX(-50%) rotate(${arrowPos === 'top' ? 225 : 45}deg)`; } + const borderStyle = `${borderColor} solid 1px;`; const styleCss = [ 'position:absolute;width:10px;height:10px;', `${positionStyle};${transformStyle};`, - `border-bottom: ${borderColor} solid 1px;`, - `border-right: ${borderColor} solid 1px;`, - `background-color: ${backgroundColor};`, - 'box-shadow: 8px 8px 16px -3px #000;' + `border-bottom:${borderStyle}`, + `border-right:${borderStyle}`, + `background-color:${backgroundColor};`, + 'box-shadow:8px 8px 16px -3px #000;' ]; return `
`; } 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; + const transitionCurve = 'cubic-bezier(0.23,1,0.32,1)'; + let transitionOption = ` ${duration / 2}s ${transitionCurve}`; + let transitionText = `opacity${transitionOption},visibility${transitionOption}`; if (!onlyFade) { + transitionOption = ` ${duration}s ${transitionCurve}`; transitionText += env.transformSupported - ? `,${TRANSFORM_VENDOR} ${duration}s ${transitionCurve}` - : `,left ${duration}s ${transitionCurve},top ${duration}s ${transitionCurve}`; + ? `,${TRANSFORM_VENDOR}${transitionOption}` + : `,left${transitionOption},top${transitionOption}`; } return CSS_TRANSITION_VENDOR + ':' + transitionText; From ae6b635937bf123d574c71d9dd6bb9fdd6c53b56 Mon Sep 17 00:00:00 2001 From: plainheart Date: Sat, 27 Feb 2021 21:36:27 +0800 Subject: [PATCH 4/8] fix(tooltip): fix tooltip position may be incorrect. --- src/component/tooltip/TooltipHTMLContent.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/component/tooltip/TooltipHTMLContent.ts b/src/component/tooltip/TooltipHTMLContent.ts index 9c1a27fbcf..81ca92813c 100644 --- a/src/component/tooltip/TooltipHTMLContent.ts +++ b/src/component/tooltip/TooltipHTMLContent.ts @@ -104,17 +104,19 @@ function assembleTransition(duration: number, onlyFade?: boolean): string { 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); + const x0 = (x - 10).toFixed(0); let y0; // not support transform, use `left` and `top` instead. if (!env.transformSupported) { - y0 = (y - el.offsetHeight / 2).toFixed(0); + y0 = y.toFixed(0); return toString ? `top:${y0}px;left:${x0}px;` : [['top', `${y0}px`], ['left', `${x0}px`]]; } // support transform - y0 = (y - zrHeight).toFixed(0); + // PENDING: don't minus `zrHeight` and keep consistent with top? + // why there is a 10px gap? + y0 = (y - zrHeight - 10).toFixed(0); const is3d = env.transform3dSupported; const translate = `translate${is3d ? '3d' : ''}(${x0}px,${y0}px${is3d ? ',0' : ''})`; return toString @@ -370,13 +372,15 @@ class TooltipHTMLContent { clearTimeout(this._longHideTimeout); const el = this.el; const style = el.style; + const styleCoord = this._styleCoord; if (!el.innerHTML) { style.display = 'none'; } else { style.cssText = gCssText + assembleCssText(tooltipModel, !this._firstShow, this._longHide) - + `${TRANSFORM_VENDOR}:${style[TRANSFORM_VENDOR as any]};` + // initial transform + + assembleTransform(el, styleCoord[0], styleCoord[1], this._zr.getHeight(), true) + `border-color:${convertToColorString(nearPointColor)};` + (tooltipModel.get('extraCssText') || '') // If mouse occasionally move over the tooltip, a mouseout event will be From 6904fc6aca31b56e5689e5360d876fcb826826fc Mon Sep 17 00:00:00 2001 From: plainheart Date: Sat, 27 Feb 2021 22:54:34 +0800 Subject: [PATCH 5/8] fix(tooltip): fix tooltip position issue when using transform. --- src/component/tooltip/TooltipHTMLContent.ts | 43 ++++++++++++--------- src/component/tooltip/helper.ts | 8 ++++ 2 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/component/tooltip/TooltipHTMLContent.ts b/src/component/tooltip/TooltipHTMLContent.ts index 81ca92813c..c2c6750fa5 100644 --- a/src/component/tooltip/TooltipHTMLContent.ts +++ b/src/component/tooltip/TooltipHTMLContent.ts @@ -31,7 +31,13 @@ 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, toCSSVendorPrefix, TRANSFORM_VENDOR, TRANSITION_VENDOR } from './helper'; +import { + shouldTooltipConfine, + toCSSVendorPrefix, + getComputedStyle, + TRANSFORM_VENDOR, + TRANSITION_VENDOR +} from './helper'; import { getPaddingFromTooltipModel } from './tooltipMarkup'; /* global document, window */ @@ -101,22 +107,24 @@ function assembleTransition(duration: number, onlyFade?: boolean): string { return CSS_TRANSITION_VENDOR + ':' + transitionText; } -function assembleTransform(el: HTMLElement, x: number, y: number, zrHeight: number, toString?: boolean) { +function assembleTransform(el: HTMLElement, x: number, y: 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 - 10).toFixed(0); + let x0; let y0; // not support transform, use `left` and `top` instead. if (!env.transformSupported) { + x0 = x.toFixed(0); y0 = y.toFixed(0); return toString ? `top:${y0}px;left:${x0}px;` : [['top', `${y0}px`], ['left', `${x0}px`]]; } // support transform - // PENDING: don't minus `zrHeight` and keep consistent with top? - // why there is a 10px gap? - y0 = (y - zrHeight - 10).toFixed(0); + // FIXME: the padding of parent element will affect the position of tooltip + const stl = getComputedStyle(el.parentElement); + x0 = (x - parseInt(stl.paddingLeft, 10)).toFixed(0); + y0 = (y - parseInt(stl.paddingTop, 10)).toFixed(0); const is3d = env.transform3dSupported; const translate = `translate${is3d ? '3d' : ''}(${x0}px,${y0}px${is3d ? ',0' : ''})`; return toString @@ -296,7 +304,8 @@ class TooltipHTMLContent { document.body.appendChild(el); } else { - container.appendChild(el); + // PENDING + container.prepend(el); } this._container = container; @@ -348,10 +357,9 @@ class TooltipHTMLContent { // FIXME // Move this logic to ec main? const container = this._container; - const stl = (container as any).currentStyle - || document.defaultView.getComputedStyle(container); + const position = getComputedStyle(container, 'position'); const domStyle = container.style; - if (domStyle.position !== 'absolute' && stl.position !== 'absolute') { + if (domStyle.position !== 'absolute' && position !== 'absolute') { domStyle.position = 'relative'; } @@ -380,7 +388,7 @@ class TooltipHTMLContent { style.cssText = gCssText + assembleCssText(tooltipModel, !this._firstShow, this._longHide) // initial transform - + assembleTransform(el, styleCoord[0], styleCoord[1], this._zr.getHeight(), true) + + assembleTransform(el, styleCoord[0], styleCoord[1], true) + `border-color:${convertToColorString(nearPointColor)};` + (tooltipModel.get('extraCssText') || '') // If mouse occasionally move over the tooltip, a mouseout event will be @@ -447,8 +455,7 @@ class TooltipHTMLContent { const style = this.el.style; const transforms = assembleTransform( this.el, - styleCoord[0], styleCoord[1], - this._zr.getHeight() + styleCoord[0], styleCoord[1] ) as string[][]; each(transforms, (transform) => { style[transform[0] as any] = transform[1]; @@ -508,12 +515,10 @@ class TooltipHTMLContent { // Consider browser compatibility. // IE8 does not support getComputedStyle. - if (document.defaultView && document.defaultView.getComputedStyle) { - const stl = document.defaultView.getComputedStyle(this.el); - if (stl) { - width += parseInt(stl.borderLeftWidth, 10) + parseInt(stl.borderRightWidth, 10); - height += parseInt(stl.borderTopWidth, 10) + parseInt(stl.borderBottomWidth, 10); - } + const stl = getComputedStyle(this.el); + if (stl) { + width += parseInt(stl.borderLeftWidth, 10) + parseInt(stl.borderRightWidth, 10); + height += parseInt(stl.borderTopWidth, 10) + parseInt(stl.borderBottomWidth, 10); } return {width: width, height: height}; diff --git a/src/component/tooltip/helper.ts b/src/component/tooltip/helper.ts index 3b6d70629b..b03befc151 100644 --- a/src/component/tooltip/helper.ts +++ b/src/component/tooltip/helper.ts @@ -56,3 +56,11 @@ export function toCSSVendorPrefix(styleVendor: string, styleProp: string) { : `-${styleVendor.slice(0, idx)}-${styleProp}`; return styleVendor.toLowerCase(); } + +export function getComputedStyle(el: HTMLElement, style?: string) { + const stl = (el as any).currentStyle + || (document.defaultView && document.defaultView.getComputedStyle(el)); + return stl + ? style ? stl[style] : stl + : null; +} From 242c403144be667dbc3dd99175a09dec82cec503 Mon Sep 17 00:00:00 2001 From: plainheart Date: Sun, 28 Feb 2021 16:18:37 +0800 Subject: [PATCH 6/8] fix(tooltip): explicitly use import type. --- src/component/tooltip/TooltipHTMLContent.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/component/tooltip/TooltipHTMLContent.ts b/src/component/tooltip/TooltipHTMLContent.ts index c2c6750fa5..85f4d8b5a1 100644 --- a/src/component/tooltip/TooltipHTMLContent.ts +++ b/src/component/tooltip/TooltipHTMLContent.ts @@ -23,14 +23,14 @@ import { normalizeEvent } from 'zrender/src/core/event'; import { transformLocalCoord } from 'zrender/src/core/dom'; import env from 'zrender/src/core/env'; import { convertToColorString, toCamelCase, normalizeCssArray } from '../../util/format'; -import ExtensionAPI from '../../core/ExtensionAPI'; -import { ZRenderType } from 'zrender/src/zrender'; -import { TooltipOption } from './TooltipModel'; +import type ExtensionAPI from '../../core/ExtensionAPI'; +import type { ZRenderType } from 'zrender/src/zrender'; +import type { TooltipOption } from './TooltipModel'; import Model from '../../model/Model'; -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 type { ZRRawEvent } from 'zrender/src/core/types'; +import type { ColorString, ZRColor } from '../../util/types'; +import type CanvasPainter from 'zrender/src/canvas/Painter'; +import type SVGPainter from 'zrender/src/svg/Painter'; import { shouldTooltipConfine, toCSSVendorPrefix, From 73a9f6e7bd0bdb5b236550d968c86841555f49ee Mon Sep 17 00:00:00 2001 From: plainheart Date: Mon, 15 Mar 2021 20:48:24 +0800 Subject: [PATCH 7/8] fix(tooltip): fix code format and check if dom is supported before `testStyle`. --- src/component/tooltip/helper.ts | 42 ++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/component/tooltip/helper.ts b/src/component/tooltip/helper.ts index b03befc151..ed78b7c908 100644 --- a/src/component/tooltip/helper.ts +++ b/src/component/tooltip/helper.ts @@ -20,6 +20,7 @@ import { TooltipOption } from './TooltipModel'; import Model from '../../model/Model'; import { toCamelCase } from '../../util/format'; +import env from 'zrender/src/core/env'; /* global document */ @@ -32,35 +33,38 @@ export function shouldTooltipConfine(tooltipModel: Model): boolea } function testStyle(styleProps: string[]): string | undefined { - const style = document.documentElement.style; - for (let i = 0, len = styleProps.length; i < len; i++) { - if (styleProps[i] in style) { - return styleProps[i]; - } - } + if (!env.domSupported) { + return; + } + 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'] + ['transform', 'webkitTransform', 'OTransform', 'MozTransform', 'msTransform'] ); export const TRANSITION_VENDOR = testStyle( - ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition'] + ['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(); + styleProp = toCamelCase(styleProp, true); + const idx = styleVendor.indexOf(styleProp); + styleVendor = idx === -1 + ? styleProp + : `-${styleVendor.slice(0, idx)}-${styleProp}`; + return styleVendor.toLowerCase(); } export function getComputedStyle(el: HTMLElement, style?: string) { - const stl = (el as any).currentStyle - || (document.defaultView && document.defaultView.getComputedStyle(el)); - return stl - ? style ? stl[style] : stl - : null; + const stl = (el as any).currentStyle + || (document.defaultView && document.defaultView.getComputedStyle(el)); + return stl + ? style ? stl[style] : stl + : null; } From e88b99f6dd7b4f5968b7ba5bf658d0369a55cc34 Mon Sep 17 00:00:00 2001 From: plainheart Date: Wed, 17 Mar 2021 16:38:17 +0800 Subject: [PATCH 8/8] fix(tooltip): fix potential NPE in `toCSSVendorPrefix` function. --- src/component/tooltip/helper.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/component/tooltip/helper.ts b/src/component/tooltip/helper.ts index ed78b7c908..fa510b9e6c 100644 --- a/src/component/tooltip/helper.ts +++ b/src/component/tooltip/helper.ts @@ -53,6 +53,9 @@ export const TRANSITION_VENDOR = testStyle( ); export function toCSSVendorPrefix(styleVendor: string, styleProp: string) { + if (!styleVendor) { + return styleProp; + } styleProp = toCamelCase(styleProp, true); const idx = styleVendor.indexOf(styleProp); styleVendor = idx === -1