Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(tooltip): fix tooltip lag issue when transition is disabled. (#16101) #16212

Merged
merged 2 commits into from
Dec 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/component/tooltip/TooltipHTMLContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*/

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';
import env from 'zrender/src/core/env';
Expand Down
91 changes: 43 additions & 48 deletions src/component/tooltip/TooltipView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@
* specific language governing permissions and limitations
* under the License.
*/
import * as zrUtil from 'zrender/src/core/util';
import { bind, each, clone, trim, isString, isFunction, isArray, isObject } from 'zrender/src/core/util';
import env from 'zrender/src/core/env';
import TooltipHTMLContent from './TooltipHTMLContent';
import TooltipRichContent from './TooltipRichContent';
import * as formatUtil from '../../util/format';
import * as numberUtil from '../../util/number';
import * as graphic from '../../util/graphic';
import { convertToColorString, formatTpl, TooltipMarker } from '../../util/format';
import { parsePercent } from '../../util/number';
import { Rect } from '../../util/graphic';
import findPointFromSeries from '../axisPointer/findPointFromSeries';
import * as layoutUtil from '../../util/layout';
import { getLayoutRect } from '../../util/layout';
import Model from '../../model/Model';
import * as globalListener from '../axisPointer/globalListener';
import * as axisHelper from '../../coord/axisHelper';
Expand All @@ -50,20 +50,15 @@ import ExtensionAPI from '../../core/ExtensionAPI';
import TooltipModel, { TooltipOption } from './TooltipModel';
import Element from 'zrender/src/Element';
import { AxisBaseModel } from '../../coord/AxisBaseModel';
// import { isDimensionStacked } from '../../data/helper/dataStackHelper';
import { ECData, getECData } from '../../util/innerStore';
import { shouldTooltipConfine } from './helper';
import { DataByCoordSys, DataByAxis } from '../axisPointer/axisTrigger';
import { normalizeTooltipFormatResult } from '../../model/mixin/dataFormat';
import { createTooltipMarkup, buildTooltipMarkup, TooltipMarkupStyleCreator } from './tooltipMarkup';
import { findEventDispatcher } from '../../util/event';
import { throttle } from '../../util/throttle';
import { clear, createOrUpdate } from '../../util/throttle';

const bind = zrUtil.bind;
const each = zrUtil.each;
const parsePercent = numberUtil.parsePercent;

const proxyRect = new graphic.Rect({
const proxyRect = new Rect({
shape: { x: -1, y: -1, width: 2, height: 2 }
});

Expand Down Expand Up @@ -137,7 +132,7 @@ type TooltipCallbackDataParams = CallbackDataParams & {
// TODO: TYPE Value type
axisValue?: string | number
axisValueLabel?: string
marker?: formatUtil.TooltipMarker
marker?: TooltipMarker
};

class TooltipView extends ComponentView {
Expand Down Expand Up @@ -168,18 +163,15 @@ class TooltipView extends ComponentView {
private _lastDataByCoordSys: DataByCoordSys[];
private _cbParamsList: TooltipCallbackDataParams[];

private _updatePosition: ReturnType<typeof throttle> | TooltipView['_doUpdatePosition'];

init(ecModel: GlobalModel, api: ExtensionAPI) {
if (env.node || !api.getDom()) {
return;
}

const tooltipModel = ecModel.getComponent('tooltip') as TooltipModel;
const renderMode = tooltipModel.get('renderMode');
this._renderMode = getTooltipRenderMode(renderMode);
const renderMode = this._renderMode = getTooltipRenderMode(tooltipModel.get('renderMode'));

this._tooltipContent = this._renderMode === 'richText'
this._tooltipContent = renderMode === 'richText'
? new TooltipRichContent(api)
: new TooltipHTMLContent(api.getDom(), api, {
appendToBody: tooltipModel.get('appendToBody', true)
Expand Down Expand Up @@ -220,14 +212,16 @@ class TooltipView extends ComponentView {

// PENDING
// `mousemove` event will be triggered very frequently when the mouse moves fast,
// which causes that the updatePosition was also called very frequently.
// In Chrome with devtools open and Firefox, tooltip looks lagged and shaked around. See #14695.
// To avoid the frequent triggering,
// consider throttling it in 50ms. (the tested result may need to validate)
this._updatePosition =
this._renderMode === 'html'
? throttle(bind(this._doUpdatePosition, this), 50)
: this._doUpdatePosition;
// which causes that the `updatePosition` function was also called frequently.
// In Chrome with devtools open and Firefox, tooltip looks laggy and shakes. See #14695 #16101
// To avoid frequent triggering,
// consider throttling it in 50ms when transition is enabled
if (this._renderMode !== 'richText' && tooltipModel.get('transitionDuration')) {
createOrUpdate(this, '_updatePosition', 50, 'fixRate');
}
else {
clear(this, '_updatePosition');
}
}

private _initGlobalListener() {
Expand Down Expand Up @@ -517,7 +511,7 @@ class TooltipView extends ComponentView {
// something. `showDelay` makes it easier to enter the content
// but tooltip do not move immediately.
const delay = tooltipModel.get('showDelay');
cb = zrUtil.bind(cb, this);
cb = bind(cb, this);
clearTimeout(this._showTimout);
delay > 0
? (this._showTimout = setTimeout(cb, delay) as any)
Expand Down Expand Up @@ -559,13 +553,13 @@ class TooltipView extends ComponentView {
);
const axisSectionMarkup = createTooltipMarkup('section', {
header: axisValueLabel,
noHeader: !zrUtil.trim(axisValueLabel),
noHeader: !trim(axisValueLabel),
sortBlocks: true,
blocks: []
});
articleMarkup.blocks.push(axisSectionMarkup);

zrUtil.each(axisItem.seriesDataIndices, function (idxItem) {
each(axisItem.seriesDataIndices, function (idxItem) {
const series = ecModel.getSeriesByIndex(idxItem.seriesIndex);
const dataIndex = idxItem.dataIndexInside;
const cbParams = series.getDataParams(dataIndex) as TooltipCallbackDataParams;
Expand All @@ -585,7 +579,7 @@ class TooltipView extends ComponentView {
// Pre-create marker style for makers. Users can assemble richText
// text in `formatter` callback and use those markers style.
cbParams.marker = markupStyleCreator.makeTooltipMarker(
'item', formatUtil.convertToColorString(cbParams.color), renderMode
'item', convertToColorString(cbParams.color), renderMode
);

const seriesTooltipResult = normalizeTooltipFormatResult(
Expand Down Expand Up @@ -681,7 +675,7 @@ class TooltipView extends ComponentView {
// Pre-create marker style for makers. Users can assemble richText
// text in `formatter` callback and use those markers style.
params.marker = markupStyleCreator.makeTooltipMarker(
'item', formatUtil.convertToColorString(params.color), renderMode
'item', convertToColorString(params.color), renderMode
);

const seriesTooltipResult = normalizeTooltipFormatResult(
Expand Down Expand Up @@ -728,7 +722,7 @@ class TooltipView extends ComponentView {
const ecData = getECData(el);
const tooltipConfig = ecData.tooltipConfig;
let tooltipOpt = tooltipConfig.option || {};
if (zrUtil.isString(tooltipOpt)) {
if (isString(tooltipOpt)) {
const content = tooltipOpt;
tooltipOpt = {
content: content,
Expand Down Expand Up @@ -766,7 +760,7 @@ class TooltipView extends ComponentView {
this._showOrMove(subTooltipModel, function (this: TooltipView) {
// Use formatterParams from element defined in component
// Avoid users modify it.
const formatterParams = zrUtil.clone(subTooltipModel.get('formatterParams') as any || {});
const formatterParams = clone(subTooltipModel.get('formatterParams') as any || {});
this._showTooltipContent(
subTooltipModel, defaultHtml, formatterParams,
asyncTicket, e.offsetX, e.offsetY, e.position, el, markupStyleCreator
Expand Down Expand Up @@ -814,17 +808,17 @@ class TooltipView extends ComponentView {
const nearPointColor = nearPoint.color;

if (formatter) {
if (zrUtil.isString(formatter)) {
if (isString(formatter)) {
const useUTC = tooltipModel.ecModel.get('useUTC');
const params0 = zrUtil.isArray(params) ? params[0] : params;
const params0 = isArray(params) ? params[0] : params;
const isTimeAxis = params0 && params0.axisType && params0.axisType.indexOf('time') >= 0;
html = formatter;
if (isTimeAxis) {
html = timeFormat(params0.axisValue, html, useUTC);
}
html = formatUtil.formatTpl(html, params, true);
html = formatTpl(html, params, true);
}
else if (zrUtil.isFunction(formatter)) {
else if (isFunction(formatter)) {
const callback = bind(function (cbTicket: string, html: string | HTMLElement | HTMLElement[]) {
if (cbTicket === this._ticket) {
tooltipContent.setContent(html, markupStyleCreator, tooltipModel, nearPointColor, positionExpr);
Expand Down Expand Up @@ -857,20 +851,20 @@ class TooltipView extends ComponentView {
): {
color: ZRColor;
} {
if (trigger === 'axis' || zrUtil.isArray(tooltipDataParams)) {
if (trigger === 'axis' || isArray(tooltipDataParams)) {
return {
color: borderColor || (this._renderMode === 'html' ? '#fff' : 'none')
};
}

if (!zrUtil.isArray(tooltipDataParams)) {
if (!isArray(tooltipDataParams)) {
return {
color: borderColor || tooltipDataParams.color || tooltipDataParams.borderColor
};
}
}

private _doUpdatePosition(
_updatePosition(
tooltipModel: Model<TooltipOption>,
positionExpr: TooltipOption['position'],
x: number, // Mouse x
Expand All @@ -890,23 +884,23 @@ class TooltipView extends ComponentView {
const rect = el && el.getBoundingRect().clone();
el && rect.applyTransform(el.transform);

if (zrUtil.isFunction(positionExpr)) {
if (isFunction(positionExpr)) {
// Callback of position can be an array or a string specify the position
positionExpr = positionExpr([x, y], params, content.el, rect, {
viewSize: [viewWidth, viewHeight],
contentSize: contentSize.slice() as [number, number]
});
}

if (zrUtil.isArray(positionExpr)) {
if (isArray(positionExpr)) {
x = parsePercent(positionExpr[0], viewWidth);
y = parsePercent(positionExpr[1], viewHeight);
}
else if (zrUtil.isObject(positionExpr)) {
else if (isObject(positionExpr)) {
const boxLayoutPosition = positionExpr as BoxLayoutOptionMixin;
boxLayoutPosition.width = contentSize[0];
boxLayoutPosition.height = contentSize[1];
const layoutRect = layoutUtil.getLayoutRect(
const layoutRect = getLayoutRect(
boxLayoutPosition, { width: viewWidth, height: viewHeight }
);
x = layoutRect.x;
Expand All @@ -917,7 +911,7 @@ class TooltipView extends ComponentView {
vAlign = null;
}
// Specify tooltip position by string 'top' 'bottom' 'left' 'right' around graphic element
else if (zrUtil.isString(positionExpr) && el) {
else if (isString(positionExpr) && el) {
const pos = calcTooltipPosition(
positionExpr, rect, contentSize, tooltipModel.get('borderWidth')
);
Expand Down Expand Up @@ -982,7 +976,7 @@ class TooltipView extends ComponentView {
});

// check is cbParams data value changed
lastCbParamsList && zrUtil.each(lastItem.seriesDataIndices, (idxItem) => {
lastCbParamsList && each(lastItem.seriesDataIndices, (idxItem) => {
const seriesIdx = idxItem.seriesIndex;
const cbParams = cbParamsList[seriesIdx];
const lastCbParams = lastCbParamsList[seriesIdx];
Expand Down Expand Up @@ -1016,6 +1010,7 @@ class TooltipView extends ComponentView {
if (env.node || !api.getDom()) {
return;
}
clear(this, '_updatePosition');
this._tooltipContent.dispose();
globalListener.unregister('itemTooltip', api);
}
Expand Down Expand Up @@ -1057,7 +1052,7 @@ function buildTooltipModel(
// value: 10,
// tooltip: 'Something you need to know'
// }
if (zrUtil.isString(tooltipOpt)) {
if (isString(tooltipOpt)) {
tooltipOpt = {
formatter: tooltipOpt
};
Expand All @@ -1072,7 +1067,7 @@ function buildTooltipModel(
}

function makeDispatchAction(payload: ShowTipPayload | HideTipPayload, api: ExtensionAPI) {
return payload.dispatchAction || zrUtil.bind(api.dispatchAction, api);
return payload.dispatchAction || bind(api.dispatchAction, api);
}

function refixTooltipPosition(
Expand Down
26 changes: 24 additions & 2 deletions test/tooltip-lag-glitch.html

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.