Skip to content

Commit

Permalink
fix(g-canvas): render should work correctly when element is clipped b…
Browse files Browse the repository at this point in the history
…y view, close #494
  • Loading branch information
dengfuping committed Jun 4, 2020
1 parent eb6a250 commit 5d85e7f
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 7 deletions.
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,6 @@
"@commitlint/config-angular": "^8.3.4",
"@types/lodash": "^4.14.119",
"@types/node": "^10.12.18",
"@types/react": "^16.9.2",
"@types/react-dom": "^16.9.0",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.4",
"benchmark": "^2.1.4",
Expand All @@ -94,4 +92,4 @@
"webpack": "^4.26.1",
"webpack-cli": "^3.1.2"
}
}
}
10 changes: 9 additions & 1 deletion packages/g-canvas/src/canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { getShape } from './util/hit';
import * as Shape from './shape';
import Group from './group';
import { applyAttrsToContext, drawChildren, getMergedRegion, mergeView } from './util/draw';
import { getPixelRatio, requestAnimationFrame, clearAnimationFrame } from './util/util';
import { each, getPixelRatio, requestAnimationFrame, clearAnimationFrame } from './util/util';

class Canvas extends AbstractCanvas {
getDefaultCfg() {
Expand Down Expand Up @@ -169,6 +169,7 @@ class Canvas extends AbstractCanvas {
// 绘制局部
_drawRegion() {
const context = this.get('context');
const refreshElements = this.get('refreshElements');
const children = this.getChildren() as IElement[];
const region = this._getRefreshRegion();
// 需要注意可能没有 region 的场景
Expand All @@ -186,6 +187,13 @@ class Canvas extends AbstractCanvas {
drawChildren(context, children, region);
context.restore();
}
each(refreshElements, (element) => {
if (element.get('hasChanged')) {
// 在视窗外的 Group 元素会加入到更新队列里,但实际却没有执行 draw() 逻辑,也就没有清除 hasChanged 标记
// 即已经重绘完、但 hasChanged 标记没有清除的元素,需要统一清除掉。主要是 Group 存在问题,具体原因待排查
element.set('hasChanged', false);
}
});
this.set('refreshElements', []);
}

Expand Down
1 change: 0 additions & 1 deletion packages/g-canvas/src/group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { IElement } from './interfaces';
import { Region } from './types';
import ShapeBase from './shape/base';
import * as Shape from './shape';
import { each, mergeRegion } from './util/util';
import { applyAttrsToContext, drawChildren, refreshElement } from './util/draw';

class Group extends AbstractGroup {
Expand Down
2 changes: 2 additions & 0 deletions packages/g-canvas/src/shape/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ class ShapeBase extends AbstractShape {
// 是否相交需要考虑 clip 的包围盒
const bbox = clip ? getMergedRegion([this, clip]) : this.getCanvasBBox();
if (!intersectRect(region, bbox)) {
// 图形的包围盒与重绘区域不相交时,也需要清除标记
this.set('hasChanged', false);
return;
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/g-canvas/tests/bugs/issue-380-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe('#380', () => {
clientY,
});

// 要模拟 drag 事件,需要触发 mousemove 事件两次以上。因为第一次 mousemove 事件是用来触发 dragstart 事件的
// 要模拟 drag 事件,需要触发 mousemove 事件两次及以上。因为第一次 mousemove 事件是用来触发 dragstart 事件的
simulateMouseEvent(el, 'mousemove', {
clientX: clientX + 20,
clientY,
Expand Down
2 changes: 1 addition & 1 deletion packages/g-canvas/tests/bugs/issue-456-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe('#456', () => {
clientY,
});
// 鼠标再次移动,才会触发 drag 事件
// 要模拟 drag 事件,需要触发 mousemove 事件两次以上。因为第一次 mousemove 事件是用来触发 dragstart 事件的
// 要模拟 drag 事件,需要触发 mousemove 事件两次及以上。因为第一次 mousemove 事件是用来触发 dragstart 事件的
simulateMouseEvent(el, 'mousemove', {
clientX: clientX + 20,
clientY,
Expand Down
120 changes: 120 additions & 0 deletions packages/g-canvas/tests/bugs/issue-494-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
const expect = require('chai').expect;
import Canvas from '../../src/canvas';
import { simulateMouseEvent, getClientPoint } from '../util';
import { getColor } from '../get-color';

const dom = document.createElement('div');
document.body.appendChild(dom);
dom.id = 'c1';
dom.style.border = '1px solid black';
dom.style.display = 'inline-block';

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

const el = canvas.get('el');
const context = canvas.get('context');
const pixelRatio = canvas.getPixelRatio();

it('render should work correctly when element is clipped by view', (done) => {
const group = canvas.addGroup();
const rect = group.addShape('rect', {
draggable: true,
attrs: {
fill: 'red',
stroke: 'blue',
lineWidth: 4,
height: 40,
width: 40,
x: 100,
y: 100,
},
});

canvas.on('click', () => {
group.translate(0, 200);
});

let origin = {};

rect.on('dragstart', (e) => {
origin = {
x: e.clientX,
y: e.clientY,
};
});

rect.on('drag', (e) => {
const clientX = +e.clientX;
const clientY = +e.clientY;
if (isNaN(clientX) || isNaN(clientY)) {
return;
}

group.translate(clientX - origin.x, clientY - origin.y);
origin = {
x: clientX,
y: clientY,
};
});

/* 先将 rect 完全拖离视窗,并点击画布 */
// 先移动到 rect 上
const { clientX, clientY } = getClientPoint(canvas, 100, 100);
simulateMouseEvent(el, 'mousemove', {
clientX,
clientY,
});
// 按下鼠标
simulateMouseEvent(el, 'mousedown', {
clientX,
clientY,
});
// 先移动 10 像素,触发 dragstart 事件
simulateMouseEvent(el, 'mousemove', {
clientX,
clientY: clientY - 10,
});
// 要模拟 drag 事件,需要触发 mousemove 事件两次及以上。因为第一次 mousemove 事件是用来触发 dragstart 事件的
simulateMouseEvent(el, 'mousemove', {
clientX,
clientY: clientY - 20,
});
// 将 rect 完全拖离视窗,这里通过鼠标离开画布足够距离进行模拟,拖拽距离共 200 = 210 - 10
simulateMouseEvent(el, 'mousemove', {
clientX,
clientY: clientY - 210,
});
// 松开鼠标,停止拖拽
simulateMouseEvent(el, 'mouseup', {
clientX,
clientY: clientY - 200,
});
let canvasBBox = group.getCanvasBBox();
// 判断 rect 是否完全拖离画布
expect(canvasBBox.maxY).eqls(-58);
// 按下鼠标
simulateMouseEvent(el, 'mousedown', {
clientX,
clientY,
});
simulateMouseEvent(el, 'mouseup', {
clientX,
clientY,
});
canvasBBox = group.getCanvasBBox();
// 判断 rect 是否移动回画布
expect(canvasBBox.maxY).eqls(142);
setTimeout(() => {
// stroke 判断
expect(getColor(context, 100 * pixelRatio, 100 * pixelRatio)).eqls('#0000ff');
// fill 判断
expect(getColor(context, (100 + 10) * pixelRatio, (100 + 10) * pixelRatio)).eqls('#ff0000');
done();
}, 25);
});
});

0 comments on commit 5d85e7f

Please sign in to comment.