From 2b9b830d6c2a6d843bc4dd17ebfa09a9931c4741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=AF=B8=E5=B2=B3?= Date: Mon, 21 Oct 2019 20:41:30 +0800 Subject: [PATCH] fix(g-svg): mouseenter and mouseleave should be effective, close #205 --- packages/g-base/src/event/event-contoller.ts | 31 +++++++++- packages/g-svg/tests/bugs/issue-205-spec.js | 60 ++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 packages/g-svg/tests/bugs/issue-205-spec.js diff --git a/packages/g-base/src/event/event-contoller.ts b/packages/g-base/src/event/event-contoller.ts index 17bd442a8..321e850b8 100644 --- a/packages/g-base/src/event/event-contoller.ts +++ b/packages/g-base/src/event/event-contoller.ts @@ -4,7 +4,7 @@ */ import GraphEvent from './graph-event'; import { ICanvas, IShape, IBase } from '../interfaces'; -import { each } from '../util/util'; +import { each, isArray } from '../util/util'; const TIME_INTERVAL = 120; // 判断拖拽和点击 const CLICK_OFFSET = 40; const DELEGATION_SPLIT = ':'; @@ -28,6 +28,15 @@ const EVENTS = [ 'contextmenu', ]; +const EVENT_MAP = { + mouseenter: 'mouseleave', + dragenter: 'dragleave', + mouseover: ['mouseout', 'mouseleave'], // 需要按照特定顺序,先触发 mouseout 事件,再触发 mouseleave 事件 + mouseleave: 'mouseenter', + dragleave: 'dragenter', + mouseout: ['mouseover', 'mouseenter'], // 需要按照特定顺序,先触发 mouseover 事件,再触发 mouseenter 事件 +}; + // 是否元素的父容器 function isParent(container, shape) { // 所有 shape 都是 canvas 的子元素 @@ -194,6 +203,16 @@ class EventController { const preShape = this.currentShape; // 如果进入、移出画布时存在图形,则要分别出发事件 if (type === 'mouseenter' || type === 'dragenter' || type === 'mouseover') { + // 从图形进入画布,preShape 上的事件最先触发 + if (preShape) { + if (isArray(EVENT_MAP[type])) { + each(EVENT_MAP[type], (item) => { + this._emitEvent(item, event, pointInfo, null, preShape, null); // 图形 => 画布 + }); + } else { + this._emitEvent(EVENT_MAP[type], event, pointInfo, null, preShape, null); // 图形 => 画布 + } + } this._emitEvent(type, event, pointInfo, null, null, shape); // 先进入画布 if (shape) { this._emitEvent(type, event, pointInfo, shape, null, shape); // 再触发图形的事件 @@ -203,6 +222,16 @@ class EventController { this._emitEvent(type, event, pointInfo, preShape, preShape, null); // 先触发图形的事件 } this._emitEvent(type, event, pointInfo, null, preShape, null); // 再触发离开画布事件 + // 从画布进入图形,shape 上的事件最后触发 + if (shape) { + if (isArray(EVENT_MAP[type])) { + each(EVENT_MAP[type], (item) => { + this._emitEvent(item, event, pointInfo, shape, null, shape); + }); + } else { + this._emitEvent(EVENT_MAP[type], event, pointInfo, shape, null, shape); + } + } } else { this._emitEvent(type, event, pointInfo, shape, null, null); // 一般事件中不需要考虑 from, to } diff --git a/packages/g-svg/tests/bugs/issue-205-spec.js b/packages/g-svg/tests/bugs/issue-205-spec.js new file mode 100644 index 000000000..6184a22e5 --- /dev/null +++ b/packages/g-svg/tests/bugs/issue-205-spec.js @@ -0,0 +1,60 @@ +const expect = require('chai').expect; +import Canvas from '../../src/canvas'; + +export function simulateMouseEvent(dom, type, cfg) { + const event = new MouseEvent(type, cfg); + dom.dispatchEvent(event); +} + +const dom = document.createElement('div'); +document.body.appendChild(dom); +dom.id = 'c1'; + +describe('#205', () => { + const canvas = new Canvas({ + container: dom, + width: 500, + height: 500, + }); + const el = canvas.get('el'); + + function getClientPoint(x, y) { + const point = canvas.getClientByPoint(x, y); + return { + clientX: point.x, + clientY: point.y, + }; + } + + it('mouseenter and mouseleave should be effective', () => { + const group = canvas.addGroup(); + const shape = group.addShape('circle', { + attrs: { + x: 100, + y: 100, + r: 40, + fill: 'red', + }, + }); + let flag = 0; + + shape.on('mouseenter', () => { + flag = 1; + }); + shape.on('mouseleave', () => { + flag = 2; + }); + + const { clientX, clientY } = getClientPoint(100, 100); + simulateMouseEvent(el, 'mouseenter', { + clientX, + clientY, + }); + expect(flag).eqls(1); + simulateMouseEvent(el, 'mouseleave', { + clientX: clientX + 50, + clientY: clientX + 50, + }); + expect(flag).eqls(2); + }); +});