Skip to content

Commit

Permalink
fix(g-base): event should work when CSS transform is applied, close #489
Browse files Browse the repository at this point in the history
 (#516)
  • Loading branch information
dengfuping authored May 10, 2020
1 parent 32ab89d commit 6e5008d
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 24 deletions.
8 changes: 8 additions & 0 deletions docs/api/canvas.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ export type Renderer = 'canvas' | 'svg';

- 修改画布大小;

### getPointByEvent(ev: Event): Point

- 根据事件对象获取画布坐标,返回类型为 `{ x: number, y: number }`

### getClientByEvent(ev: Event): Point

- 根据事件对象获取窗口坐标,返回类型为 `{ x: number, y: number }`

### getPointByClient(clientX: number, clientY: number)

- 根据窗口坐标,获取对应的画布坐标,返回类型为 `{ x: number, y: number }`
Expand Down
8 changes: 8 additions & 0 deletions docs/api/canvas.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ export type Renderer = 'canvas' | 'svg';

- 修改画布大小;

### getPointByEvent(ev: Event): Point

- 根据事件对象获取画布坐标,返回类型为 `{ x: number, y: number }`

### getClientByEvent(ev: Event): Point

- 根据事件对象获取窗口坐标,返回类型为 `{ x: number, y: number }`

### getPointByClient(clientX: number, clientY: number)

- 根据窗口坐标,获取对应的画布坐标,返回类型为 `{ x: number, y: number }`
Expand Down
3 changes: 2 additions & 1 deletion packages/g-base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@
"@types/d3-timer": "^1.0.9",
"d3-ease": "^1.0.5",
"d3-interpolate": "^1.3.2",
"d3-timer": "^1.0.9"
"d3-timer": "^1.0.9",
"detect-browser": "^5.1.0"
},
"__npminstall_done": false
}
49 changes: 48 additions & 1 deletion packages/g-base/src/abstract/canvas.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { detect } from 'detect-browser';
import Container from './container';
import { ICanvas } from '../interfaces';
import { CanvasCfg, Point, Renderer, Cursor } from '../types';
import { isBrowser, isString } from '../util/util';
import { isBrowser, isNil, isString } from '../util/util';
import Timeline from '../animate/timeline';
import EventController from '../event/event-contoller';

const PX_SUFFIX = 'px';

const browser = detect();
const isFirefox = browser && browser.name === 'firefox';

abstract class Canvas extends Container implements ICanvas {
constructor(cfg: CanvasCfg) {
super(cfg);
Expand All @@ -20,6 +24,8 @@ abstract class Canvas extends Container implements ICanvas {
const cfg = super.getDefaultCfg();
// set default cursor style for canvas
cfg['cursor'] = 'default';
// CSS transform 目前尚未经过长时间验证,为了避免影响上层业务,默认关闭,上层按需开启
cfg['supportCSSTransform'] = false;
return cfg;
}

Expand Down Expand Up @@ -127,6 +133,47 @@ abstract class Canvas extends Container implements ICanvas {
}
}

// 实现接口
getPointByEvent(ev: Event): Point {
const supportCSSTransform = this.get('supportCSSTransform');
if (supportCSSTransform) {
// For Firefox <= 38
if (isFirefox && !isNil((ev as any).layerX) && (ev as any).layerX !== (ev as MouseEvent).offsetX) {
return {
x: (ev as any).layerX,
y: (ev as any).layerY,
};
}
if (!isNil((ev as MouseEvent).offsetX)) {
// For IE6+, Firefox >= 39, Chrome, Safari, Opera
return {
x: (ev as MouseEvent).offsetX,
y: (ev as MouseEvent).offsetY,
};
}
}
// should calculate by self for other cases, like Safari in ios
// TODO: support CSS transform
const { x: clientX, y: clientY } = this.getClientByEvent(ev);
return this.getPointByClient(clientX, clientY);
}

// 获取 touch 事件的 clientX 和 clientY 需要单独处理
getClientByEvent(ev: Event) {
let clientInfo: MouseEvent | Touch = event as MouseEvent;
if ((ev as TouchEvent).touches) {
if (ev.type === 'touchend') {
clientInfo = (ev as TouchEvent).changedTouches[0];
} else {
clientInfo = (ev as TouchEvent).touches[0];
}
}
return {
x: clientInfo.clientX,
y: clientInfo.clientY,
};
}

// 实现接口
getPointByClient(clientX: number, clientY: number): Point {
const el = this.get('el');
Expand Down
24 changes: 4 additions & 20 deletions packages/g-base/src/event/event-contoller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,6 @@ const EVENTS = [
'mousewheel',
];

// 触摸事件的 clientX,clientY 获取有一定差异
function getClientPoint(event) {
let clientInfo = event;
if (event.touches) {
if (event.type === 'touchend') {
clientInfo = event.changedTouches[0];
} else {
clientInfo = event.touches[0];
}
}
return {
clientX: clientInfo.clientX,
clientY: clientInfo.clientY,
};
}

// 是否有委托事件监听
function hasDelegation(events, type) {
for (const key in events) {
Expand Down Expand Up @@ -169,13 +153,13 @@ class EventController {
// 获取事件的当前点的信息
_getPointInfo(ev) {
const canvas = this.canvas;
const clientPoint = getClientPoint(ev);
const point = canvas.getPointByClient(clientPoint.clientX, clientPoint.clientY);
const clientPoint = canvas.getClientByEvent(ev);
const point = canvas.getPointByEvent(ev);
return {
x: point.x,
y: point.y,
clientX: clientPoint.clientX,
clientY: clientPoint.clientY,
clientX: clientPoint.x,
clientY: clientPoint.y,
};
}

Expand Down
18 changes: 16 additions & 2 deletions packages/g-base/src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -582,10 +582,24 @@ export interface ICanvas extends IContainer {
changeSize(width: number, height: number);

/**
* 将窗口坐标转变成 canvas 坐标
* 根据事件对象获取画布坐标
* @param {Event} ev 事件对象
* @return {object} 画布坐标
*/
getPointByEvent(ev: Event): Point;

/**
* 根据事件对象获取窗口坐标
* @param {Event} ev 事件对象
* @return {object} 窗口坐标
*/
getClientByEvent(ev: Event): Point;

/**
* 将窗口坐标转变成画布坐标
* @param {number} clientX 窗口 x 坐标
* @param {number} clientY 窗口 y 坐标
* @return {object} canvas坐标
* @return {object} 画布坐标
*/
getPointByClient(clientX: number, clientY: number): Point;

Expand Down
58 changes: 58 additions & 0 deletions packages/g-canvas/tests/bugs/issue-489-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// // CSS transform 的单测在 interactive 和 renderer 的模式下表现不一致,因此目前没有想到比较好的方式去测试,先全部注释掉
// // 已经在 interactive 模式下手动测试通过,CSS transform 下的事件表现是 OK 的
// const expect = require('chai').expect;
// import Canvas from '../../src/canvas';
// import { simulateMouseEvent, getClientPoint } from '../util';

// const dom = document.createElement('div');
// document.body.appendChild(dom);
// // 支持嵌套的 CSS transform
// document.body.style.transform = 'translate(200px)';
// dom.id = 'c1';
// dom.style.border = '1px solid red';
// // transform 2d 和 3d 函数都支持
// dom.style.transform = 'translate(100px, 100px) scale(1.2) rotate3d(1, 1, 1, 45deg)';

// describe('#489', () => {
// const canvas = new Canvas({
// container: dom,
// width: 400,
// height: 400,
// supportCSSTransform: true,
// });

// const el = canvas.get('el');

// it('event should work when CSS transform is applied', () => {
// const rect = canvas.addShape('rect', {
// attrs: {
// x: 50,
// y: 50,
// width: 50,
// height: 50,
// fill: 'red',
// },
// });
// let clickCalled = false;
// rect.on('click', () => {
// clickCalled = true;
// });
// rect.on('mouseenter', () => {
// rect.attr('fill', 'blue');
// });
// rect.on('mouseleave', () => {
// rect.attr('fill', 'red');
// });

// const { clientX, clientY } = getClientPoint(canvas, 75, 75);
// simulateMouseEvent(el, 'mousedown', {
// clientX,
// clientY,
// });
// simulateMouseEvent(el, 'mouseup', {
// clientX,
// clientY,
// });
// expect(clickCalled).equals(true);
// });
// });

0 comments on commit 6e5008d

Please sign in to comment.