Skip to content

Commit

Permalink
Merge pull request #2209 from antvis/tooltip-pointer-events
Browse files Browse the repository at this point in the history
feat(tooltip): 通过 pointer-events 属性来达到 tooltip 躲避鼠标的交互效果
  • Loading branch information
dxq613 committed Mar 23, 2020
2 parents d835f18 + 8d3b0d5 commit 4a48885
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 86 deletions.
2 changes: 2 additions & 0 deletions examples/gallery/dynamic/demo/dynamic-bar.ts
Expand Up @@ -98,6 +98,7 @@ fetch('../data/china-gdp.json')
fill: '#ddd',
textAlign: 'end'
},
animate: false,
});
chart
.interval()
Expand Down Expand Up @@ -145,6 +146,7 @@ fetch('../data/china-gdp.json')
fill: '#ddd',
textAlign: 'end'
},
animate: false,
});
// @ts-ignore
chart.changeData(handleData(Object.values(data)[count]));
Expand Down
4 changes: 2 additions & 2 deletions examples/gallery/dynamic/demo/dynamic-bubble.ts
Expand Up @@ -110,7 +110,7 @@ fetch('../data/life.json')
.shape('circle')
.animate({
update: {
duration: 300,
duration: 200,
easing: 'easeLinear'
}
})
Expand Down Expand Up @@ -159,5 +159,5 @@ fetch('../data/life.json')
}

countUp();
interval = setInterval(countUp, 300);
interval = setInterval(countUp, 200);
});
69 changes: 42 additions & 27 deletions src/chart/controller/tooltip.ts
@@ -1,4 +1,6 @@
import { deepMix, each, find, flatten, get, isArray, isEqual, isFunction, mix } from '@antv/util';
import { CONTAINER_CLASS } from '@antv/component/lib/tooltip/css-const';

import { deepMix, each, find, flatten, get, isArray, isEqual, isFunction, mix, isUndefined } from '@antv/util';
import { Crosshair, HtmlTooltip, IGroup } from '../../dependents';
import Geometry from '../../geometry/base';
import { MappingDatum, Point, TooltipOption } from '../../interface';
Expand Down Expand Up @@ -35,6 +37,7 @@ export default class Tooltip extends Controller<TooltipOption> {
private yCrosshair;
private guideGroup: IGroup;

private isLocked: boolean = false;
private isVisible: boolean = true;
private items;
private title: string;
Expand All @@ -46,8 +49,8 @@ export default class Tooltip extends Controller<TooltipOption> {
public init() { }

public render() {
this.option = this.view.getOptions().tooltip;
this.isVisible = this.option !== false;
const option = this.view.getOptions().tooltip;
this.isVisible = option !== false;
}

/**
Expand Down Expand Up @@ -155,6 +158,36 @@ export default class Tooltip extends Controller<TooltipOption> {
this.view.emit('tooltip:hide', {});
}

/**
* lockTooltip
*/
public lockTooltip() {
this.isLocked = true;
if (this.tooltip) {
// tooltip contianer 可捕获事件
this.tooltip.setCapture(true);
}
}

/**
* unlockTooltip
*/
public unlockTooltip() {
this.isLocked = false;
const cfg = this.getTooltipCfg();
if (this.tooltip) {
// 重置 capture 属性
this.tooltip.setCapture(cfg.capture);
}
}

/**
* isTooltipLocked
*/
public isTooltipLocked() {
return this.isLocked;
}

public clear() {
const { tooltip, xCrosshair, yCrosshair, tooltipMarkersGroup } = this;
if (tooltip) {
Expand Down Expand Up @@ -198,6 +231,7 @@ export default class Tooltip extends Controller<TooltipOption> {
this.yCrosshair = null;
this.tooltip = null;
this.guideGroup = null;
this.isLocked = false;
}

public changeVisible(visible: boolean) {
Expand Down Expand Up @@ -272,17 +306,18 @@ export default class Tooltip extends Controller<TooltipOption> {
public layout() { }
public update() {
this.clear();
// 更新 tooltip 配置
this.option = this.view.getOptions().tooltip;
}

// 获取 tooltip 配置,因为用户可能会通过 view.tooltip() 重新配置 tooltip,所以就不做缓存,每次直接读取
private getTooltipCfg() {
const view = this.view;
const option = this.option;
const option = view.getOptions().tooltip;
const theme = view.getTheme();
const defaultCfg = get(theme, ['components', 'tooltip'], {});
return deepMix({}, defaultCfg, option);
const enterable = isUndefined(get(option, 'enterable')) ? defaultCfg.enterable : get(option, 'enterable');
return deepMix({}, defaultCfg, option, {
capture: enterable || this.isLocked ? true : false,
});
}

private getTitle(items) {
Expand All @@ -309,26 +344,6 @@ export default class Tooltip extends Controller<TooltipOption> {
});

tooltip.init();

const tooltipContainer = tooltip.get('container');
if (cfg.enterable === false) {
// 优化体验,在 tooltip dom 上加绑事件
// 如果 tooltip 不允许进入
tooltipContainer.onmousemove = event => {
// 避免 tooltip 频繁闪烁
const point = this.view.getCanvas().getPointByClient(event.clientX, event.clientY);
this.view.emit('plot:mousemove', point);
};
}

// 优化:鼠标移入 tooltipContainer 然后再移出时,需要隐藏 tooltip
tooltipContainer.onmouseleave = () => {
if (!this.view.isTooltipLocked()) {
this.hideTooltip();
}
};


this.tooltip = tooltip;
}

Expand Down
15 changes: 10 additions & 5 deletions src/chart/view.ts
Expand Up @@ -134,8 +134,6 @@ export class View extends Base {

/** 当前鼠标是否在 plot 内(CoordinateBBox) */
private isPreMouseInPlot: boolean = false;
/** tooltip 是否被锁定 */
private tooltipLocked: boolean;
/** 默认标识位,用于判定数据是否更新 */
private isDataChanged: boolean = false;

Expand Down Expand Up @@ -1012,7 +1010,10 @@ export class View extends Base {
* @returns View
*/
public lockTooltip(): View {
this.tooltipLocked = true;
const tooltip = this.getController('tooltip') as TooltipComponent;
if (tooltip) {
tooltip.lockTooltip();
}
return this;
}

Expand All @@ -1021,7 +1022,10 @@ export class View extends Base {
* @returns View
*/
public unlockTooltip(): View {
this.tooltipLocked = false;
const tooltip = this.getController('tooltip') as TooltipComponent;
if (tooltip) {
tooltip.unlockTooltip();
}
return this;
}

Expand All @@ -1030,7 +1034,8 @@ export class View extends Base {
* @returns 是否锁定
*/
public isTooltipLocked() {
return this.tooltipLocked;
const tooltip = this.getController('tooltip') as TooltipComponent;
return tooltip && tooltip.isTooltipLocked();
}

/**
Expand Down
71 changes: 19 additions & 52 deletions tests/bugs/tooltip-spec.ts
Expand Up @@ -90,45 +90,6 @@ describe('tooltip', () => {
expect(tooltipItems.length).toBe(1);
});

it('tooltip avoid', () => {
const data = [
{ year: '1991', value: 15468 },
{ year: '1992', value: 16100 },
{ year: '1993', value: 15900 },
{ year: '1994', value: 17409 },
{ year: '1995', value: 17000 },
{ year: '1996', value: 31056 },
{ year: '1997', value: 31982 },
{ year: '1998', value: 32040 },
{ year: '1999', value: 33233 },
];
const chart = new Chart({
container: createDiv(),
width: 400,
height: 250,
});

chart.data(data);
chart.area().position('year*value');

const moveEvent = jest.fn();
chart.on('plot:mousemove', moveEvent);

chart.render();

const point = chart.getXY({ year: '1995', value: 17000 });
chart.showTooltip(point);

const tooltip = chart.ele.getElementsByClassName('g2-tooltip')[0];
const mousemoveEvent = new MouseEvent('mousemove', {
clientX: 100,
clientY: 100,
});
tooltip.dispatchEvent(mousemoveEvent);

expect(moveEvent).toBeCalled();
});

it('heatmap tooltip', () => {
const chart = new Chart({
container: createDiv(),
Expand Down Expand Up @@ -157,7 +118,7 @@ describe('tooltip', () => {
expect(tooltip).toBeDefined();
});

it('tooltip hide when mouseleave tooltipContainer', () => {
it('tooltip avoid', () => {
const data = [
{ year: '1991', value: 15468 },
{ year: '1992', value: 16100 },
Expand All @@ -177,28 +138,34 @@ describe('tooltip', () => {

chart.data(data);
chart.area().position('year*value');

const moveEvent = jest.fn();
chart.on('plot:mousemove', moveEvent);

chart.render();

const point = chart.getXY({ year: '1995', value: 17000 });
chart.showTooltip(point);

const tooltip = chart.ele.getElementsByClassName('g2-tooltip')[0];
const mousemoveEvent = new MouseEvent('mouseleave', {
clientX: 100,
clientY: 100,
// @ts-ignore
expect(chart.ele.getElementsByClassName('g2-tooltip')[0].style['pointer-events']).toBe('none');

chart.tooltip({
enterable: true,
});
tooltip.dispatchEvent(mousemoveEvent);
chart.hideTooltip();
chart.showTooltip(chart.getXY({ year: '1992', value: 16100 }));
// @ts-ignore
expect(tooltip.style.visibility).toBe('hidden');
expect(chart.ele.getElementsByClassName('g2-tooltip')[0].style['pointer-events']).toBe('auto');

chart.hideTooltip();
chart.tooltip({
enterable: false,
});
chart.lockTooltip();
chart.showTooltip(point);
tooltip.dispatchEvent(mousemoveEvent);
// @ts-ignore
expect(tooltip.style.visibility).toBe('visible');
expect(chart.ele.getElementsByClassName('g2-tooltip')[0].style['pointer-events']).toBe('auto');

chart.unlockTooltip();
chart.showTooltip(chart.getXY({ year: '1992', value: 16100 }));
// @ts-ignore
expect(chart.ele.getElementsByClassName('g2-tooltip')[0].style['pointer-events']).toBe('none');
});
});
4 changes: 4 additions & 0 deletions tests/unit/chart/view/index-spec.ts
Expand Up @@ -315,11 +315,15 @@ describe('View', () => {
it('lockTooltip', () => {
view.lockTooltip();
expect(view.isTooltipLocked()).toBeTrue();
// @ts-ignore
expect(div.getElementsByClassName('g2-tooltip')[0].style['pointer-events']).toBe('auto');
});

it('unlockTooltip', () => {
view.unlockTooltip();
expect(view.isTooltipLocked()).toBeFalse();
// @ts-ignore
expect(div.getElementsByClassName('g2-tooltip')[0].style['pointer-events']).toBe('none');
});

it('filtered group scale values', () => {
Expand Down

0 comments on commit 4a48885

Please sign in to comment.