Skip to content

Commit

Permalink
feat(g-base): support * for event delegation, add name and delegateOb…
Browse files Browse the repository at this point in the history
…ject for event, close #249
  • Loading branch information
dengfuping committed Nov 13, 2019
1 parent 859da22 commit c170886
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 17 deletions.
6 changes: 0 additions & 6 deletions packages/g-base/src/abstract/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,6 @@ abstract class Base extends EE implements IBase {
* @type {object}
*/
cfg: object;
/**
* @private
* 事件集合
* @type {object}
*/
events: object = {};

/**
* 是否被销毁
Expand Down
34 changes: 25 additions & 9 deletions packages/g-base/src/event/event-contoller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { each, isArray } from '../util/util';
const TIME_INTERVAL = 120; // 判断拖拽和点击
const CLICK_OFFSET = 40;
const DELEGATION_SPLIT = ':';
const WILDCARD = '*';

const EVENTS = [
'mousedown',
Expand Down Expand Up @@ -81,20 +82,34 @@ function hasDelegation(events, type) {
return false;
}

// 触发目标事件,目标只能是 shape 或 canvas
function emitTargetEvent(target, type, eventObj) {
eventObj.name = type;
eventObj.target = target;
eventObj.currentTarget = target;
eventObj.delegateTarget = target;
target.emit(type, eventObj);
}

// 触发委托事件
function emitDelegation(container, type, eventObj) {
const paths = eventObj.propagationPath;
const events = container.getEvents();
// 至少有一个对象
// 至少有一个对象,且第一个对象为 shape
for (let i = 0; i < paths.length; i++) {
const element = paths[i];
// 暂定跟 name 绑定
const name = element.get('name');
if (name) {
// 事件委托的形式 name:type
const eventName = name + DELEGATION_SPLIT + type;
if (events[eventName]) {
eventObj.delegateTarget = container;
if (events[eventName] || events[WILDCARD]) {
// 对于通配符 *,事件名称 = 委托事件名称
eventObj.name = eventName;
eventObj.currentTarget = element;
eventObj.delegateTarget = container;
// 将委托事件的监听对象 delegateObject 挂载到事件对象上
eventObj.delegateObject = element.get('delegateObject');
container.emit(eventName, eventObj);
}
}
Expand Down Expand Up @@ -123,8 +138,10 @@ function bubbleEvent(container, type, eventObj) {
eventObj.bubbles = false;
return;
}
// 绑定事件的对象
// 事件名称可能在委托过程中被修改,因此事件冒泡时需要重新设置事件名称
eventObj.name = type;
eventObj.currentTarget = container;
eventObj.delegateTarget = container;
container.emit(type, eventObj);
}
}
Expand Down Expand Up @@ -158,7 +175,6 @@ class EventController {

_getEventObj(type, event, point, target, fromShape, toShape) {
const eventObj = new GraphEvent(type, event);
// eventObj.target = target;
eventObj.fromShape = fromShape;
eventObj.toShape = toShape;
eventObj.x = point.x;
Expand Down Expand Up @@ -366,9 +382,9 @@ class EventController {
const eventObj = this._getEventObj(type, event, pointInfo, shape, fromShape, toShape);
// 存在 shape 触发,则进行冒泡处理
if (shape) {
eventObj.target = shape;
eventObj.shape = shape;
shape.emit(type, eventObj);
// 触发 shape 上的事件
emitTargetEvent(shape, type, eventObj);
let parent = shape.getParent();
// 执行冒泡
while (parent) {
Expand All @@ -384,8 +400,8 @@ class EventController {
} else {
// 如果没有 shape 直接在 canvas 上触发
const canvas = this.canvas;
eventObj.target = canvas;
canvas.emit(type, eventObj);
// 直接触发 canavas 上的事件
emitTargetEvent(canvas, type, eventObj);
}
}

Expand Down
20 changes: 20 additions & 0 deletions packages/g-base/src/event/graph-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ class GraphEvent {
* @type {string}
*/
type: string;
/**
* 事件名称
* @type {string}
*/
name: string;
/**
* 画布上的位置 x
* @type {number}
Expand Down Expand Up @@ -41,6 +46,16 @@ class GraphEvent {
* @type {object}
*/
currentTarget: object = null;
/**
* 委托对象
* @type {object}
*/
delegateTarget: object = null;
/**
* 委托事件监听对象的代理对象,即 ev.delegateObject = ev.currentTarget.get('delegateObject')
* @type {object}
*/
delegateObject: object = null;
/**
* 是否阻止了原生事件
* @type {boolean}
Expand Down Expand Up @@ -84,6 +99,7 @@ class GraphEvent {

constructor(type, event) {
this.type = type;
this.name = type;
this.domEvent = event;
this.timeStamp = event.timeStamp;
}
Expand All @@ -107,6 +123,10 @@ class GraphEvent {
const type = this.type;
return `[Event (type=${type})]`;
}

save() {}

restore() {}
}

export default GraphEvent;
4 changes: 2 additions & 2 deletions packages/g-base/tests/unit/event-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ describe('test event object', () => {
expect(event.defaultPrevented).eql(true);
});

it('stopProgation', () => {
it('stopPropagation', () => {
event.stopPropagation();
expect(event.propagationStopped).eql(true);
});
Expand Down Expand Up @@ -829,7 +829,7 @@ describe('test graphic events', () => {
group1.set('name', null);
});

it('stopProgation', () => {
it('stopPropagation', () => {
let group1Called = false;
let group11Called = false;
let canvasCalled = false;
Expand Down
182 changes: 182 additions & 0 deletions packages/g-canvas/tests/bugs/issue-249-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
const expect = require('chai').expect;
import Canvas from '../../src/canvas';
import { simulateMouseEvent, getClientPoint } from '../util';

const dom = document.createElement('div');
document.body.appendChild(dom);
dom.id = 'c1';

describe('#249', () => {
it('event attrs should be correct when emit event on shape', (done) => {
const canvas = new Canvas({
container: dom,
width: 600,
height: 600,
});
const el = canvas.get('el');
const groupDelegateObject = { a: 1, b: 2 };
const circleDelegateObject = { x: 1, y: 2 };
const group = canvas.addGroup({
name: 'group',
delegateObject: groupDelegateObject,
});
const circle = group.addShape('circle', {
name: 'circle',
delegateObject: circleDelegateObject,
attrs: {
x: 50,
y: 50,
r: 50,
fill: 'red',
},
});

// canvas * 事件触发的次数
let canvasEventCount = 0;
// group * 事件触发的次数
let groupEventCount = 0;

// 共触发 3 次
canvas.on('*', (e) => {
canvasEventCount++;
// 事件会触发多次,且每次的 e.name 都不同
if (canvasEventCount === 1) {
expect(e.name).eqls('circle:mousedown');
expect(e.currentTarget).eqls(circle);
expect(e.delegateObject).eqls(circleDelegateObject);
} else if (canvasEventCount === 2) {
expect(e.name).eqls('group:mousedown');
expect(e.currentTarget).eqls(group);
expect(e.delegateObject).eqls(groupDelegateObject);
} else if (canvasEventCount === 3) {
expect(e.name).eqls('mousedown');
expect(e.currentTarget).eqls(canvas);
}
expect(e.type).eqls('mousedown');
expect(e.target).eqls(circle);
expect(e.delegateTarget).eqls(canvas);
});

canvas.on('mousedown', (e) => {
expect(e.name).eqls('mousedown');
expect(e.type).eqls('mousedown');
expect(e.target).eqls(circle);
expect(e.currentTarget).eqls(canvas);
expect(e.delegateTarget).eqls(canvas);
});

canvas.on('group:mousedown', (e) => {
expect(e.name).eqls('group:mousedown');
expect(e.type).eqls('mousedown');
expect(e.target).eqls(circle);
expect(e.currentTarget).eqls(group);
expect(e.delegateTarget).eqls(canvas);
expect(e.delegateObject).eqls(groupDelegateObject);
});

canvas.on('circle:mousedown', (e) => {
expect(e.name).eqls('circle:mousedown');
expect(e.type).eqls('mousedown');
expect(e.target).eqls(circle);
expect(e.currentTarget).eqls(circle);
expect(e.delegateTarget).eqls(canvas);
expect(e.delegateObject).eqls(circleDelegateObject);
});

// 共触发 2 次
group.on('*', (e) => {
groupEventCount++;
if (groupEventCount === 1) {
expect(e.name).eqls('circle:mousedown');
expect(e.currentTarget).eqls(circle);
expect(e.delegateObject).eqls(circleDelegateObject);
} else if (groupEventCount === 2) {
expect(e.name).eqls('mousedown');
expect(e.currentTarget).eqls(group);
}
expect(e.type).eqls('mousedown');
expect(e.target).eqls(circle);
expect(e.delegateTarget).eqls(group);
});

group.on('mousedown', (e) => {
expect(e.name).eqls('mousedown');
expect(e.type).eqls('mousedown');
expect(e.target).eqls(circle);
expect(e.currentTarget).eqls(group);
expect(e.delegateTarget).eqls(group);
});

group.on('circle:mousedown', (e) => {
expect(e.name).eqls('circle:mousedown');
expect(e.type).eqls('mousedown');
expect(e.target).eqls(circle);
expect(e.currentTarget).eqls(circle);
expect(e.delegateTarget).eqls(group);
expect(e.delegateObject).eqls(circleDelegateObject);
});

circle.on('mousedown', (e) => {
expect(e.name).eqls('mousedown');
expect(e.type).eqls('mousedown');
expect(e.target).eqls(circle);
expect(e.currentTarget).eqls(circle);
expect(e.delegateTarget).eqls(circle);
});

// emit event on shape
const { clientX, clientY } = getClientPoint(canvas, 50, 50);
simulateMouseEvent(el, 'mousedown', {
clientX,
clientY,
});
setTimeout(() => {
done();
}, 500);
});

it('event attrs should be correct when emit event on canvas', () => {
const canvas = new Canvas({
container: dom,
width: 600,
height: 600,
});

const el = canvas.get('el');
const group = canvas.addGroup({
name: 'group',
});
group.addShape('circle', {
name: 'circle',
attrs: {
x: 50,
y: 50,
r: 50,
fill: 'red',
},
});

canvas.on('*', (e) => {
expect(e.name).eqls('mousedown');
expect(e.type).eqls('mousedown');
expect(e.target).eqls(canvas);
expect(e.currentTarget).eqls(canvas);
expect(e.delegateTarget).eqls(canvas);
});

canvas.on('mousedown', (e) => {
expect(e.name).eqls('mousedown');
expect(e.type).eqls('mousedown');
expect(e.target).eqls(canvas);
expect(e.currentTarget).eqls(canvas);
expect(e.delegateTarget).eqls(canvas);
});

// emit event on canvas
const { clientX, clientY } = getClientPoint(canvas, 100, 100);
simulateMouseEvent(el, 'mousedown', {
clientX,
clientY,
});
});
});

0 comments on commit c170886

Please sign in to comment.