Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6730730

Browse files
committed
fix(dirty-rect): bug when elements have multiple zlevel
1 parent 8a2d921 commit 6730730

4 files changed

Lines changed: 92 additions & 38 deletions

File tree

src/canvas/Layer.ts

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,10 @@ export default class Layer extends Eventful {
105105
__startIndex = 0
106106
__endIndex = 0
107107

108+
// indices in the previous frame
109+
__prevStartIndex: number = null
110+
__prevEndIndex: number = null
111+
108112
__builtin__: boolean
109113

110114
constructor(id: string | HTMLCanvasElement, painter: CanvasPainter, dpr?: number) {
@@ -149,6 +153,11 @@ export default class Layer extends Eventful {
149153
return this.__endIndex - this.__startIndex;
150154
}
151155

156+
afterBrush() {
157+
this.__prevStartIndex = this.__startIndex;
158+
this.__prevEndIndex = this.__endIndex;
159+
}
160+
152161
initContext() {
153162
this.ctx = this.dom.getContext('2d');
154163
(this.ctx as ZRCanvasRenderingContext).dpr = this.dpr;
@@ -165,23 +174,30 @@ export default class Layer extends Eventful {
165174
}
166175
}
167176

168-
createRepaintRects(displayList: Displayable[], prevList: Displayable[]) {
177+
/**
178+
* Create repaint list when using dirty rect rendering.
179+
*
180+
* @param layer onlu elements in this layer will be used to compute
181+
* @param displayList current rendering list
182+
* @param prevList last frame rendering list
183+
* @return repaint rects. null for the first frame, [] for no element dirty
184+
*/
185+
createRepaintRects(
186+
layer: Layer,
187+
displayList: Displayable[],
188+
prevList: Displayable[]
189+
) {
169190
if (this.__firstTimePaint) {
170-
util.each(displayList, el => {
171-
if (el.__dirty) {
172-
el.setPrevPaintRect(el.getPaintRect());
173-
}
174-
});
175-
176191
this.__firstTimePaint = false;
177-
return [];
192+
return null;
178193
}
179194

180195
const mergedRepaintRects: BoundingRect[] = [];
181196
const rects: BoundingRect[] = [];
182197

183198
// Add current and previous bounding rect
184-
util.each(displayList, el => {
199+
for (let i = layer.__startIndex; i < layer.__endIndex; ++i) {
200+
const el = displayList[i];
185201
if (el.__dirty) {
186202
el.__needsRepaintDirtyRect = false;
187203

@@ -193,21 +209,21 @@ export default class Layer extends Eventful {
193209
const curRect = el.getPaintRect();
194210
if (isValidPaintRect(curRect)) {
195211
rects.push(curRect);
196-
el.setPrevPaintRect(curRect);
197212
}
198213
}
199-
});
214+
}
200215

201216
// Add removed displayables because they need to be cleared
202-
util.each(prevList, el => {
217+
for (let i = layer.__prevStartIndex; i < layer.__prevEndIndex; ++i) {
218+
const el = prevList[i];
203219
if (el.__needsRepaintDirtyRect) {
204220
// el is removed
205221
const prevRect = el.getPrevPaintRect();
206222
if (isValidPaintRect(prevRect)) {
207223
rects.push(prevRect);
208224
}
209225
}
210-
});
226+
}
211227

212228
// Merge
213229
util.each(rects, rect => {
@@ -395,11 +411,11 @@ export default class Layer extends Eventful {
395411
}
396412
};
397413

398-
if (!repaintRects || !repaintRects.length) {
414+
if (!repaintRects) {
399415
// Clear the full canvas
400416
doClear(new BoundingRect(0, 0, width, height));
401417
}
402-
else {
418+
else if (repaintRects.length) {
403419
// Clear the repaint areas
404420
util.each(repaintRects, rect => {
405421
doClear(new BoundingRect(

src/canvas/Painter.ts

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,11 @@ export default class CanvasPainter implements PainterBase {
334334
self._paintList(list, prevList, paintAll, redrawId);
335335
});
336336
}
337+
else {
338+
util.each(this._layers, layer => {
339+
layer.afterBrush();
340+
});
341+
}
337342
}
338343

339344
private _compositeManually() {
@@ -349,7 +354,12 @@ export default class CanvasPainter implements PainterBase {
349354
});
350355
}
351356

352-
private _doPaintList(list: Displayable[], prevList: Displayable[], paintAll?: boolean): {
357+
private _doPaintList(
358+
list: Displayable[],
359+
prevList: Displayable[],
360+
paintAll?: boolean,
361+
useDirtyRect?: boolean
362+
): {
353363
finished: boolean
354364
needsRefreshHover: boolean
355365
} {
@@ -370,11 +380,15 @@ export default class CanvasPainter implements PainterBase {
370380
let finished = true;
371381
let needsRefreshHover = false;
372382

383+
// TODO:
384+
useDirtyRect = true;
385+
373386
for (let k = 0; k < layerList.length; k++) {
374387
const layer = layerList[k];
375388
const ctx = layer.ctx;
376389

377-
const repaintRects = layer.createRepaintRects(list, prevList);
390+
const repaintRects = useDirtyRect
391+
&& layer.createRepaintRects(layer, list, prevList);
378392

379393
ctx.save();
380394

@@ -436,7 +450,7 @@ export default class CanvasPainter implements PainterBase {
436450
}
437451
};
438452

439-
if (repaintRects.length) {
453+
if (repaintRects) {
440454
// Set repaintRect as clipPath
441455
for (var r = 0; r < repaintRects.length; ++r) {
442456
const rect = repaintRects[r];
@@ -492,17 +506,12 @@ export default class CanvasPainter implements PainterBase {
492506
isLast: boolean
493507
) {
494508
const ctx = currentLayer.ctx;
495-
if (repaintRect) {
496-
// If there is repaintRect, only render the intersected ones
497-
if (el.getPaintRect().intersect(repaintRect)) {
498-
// console.log('rebrush', el.id);
499-
brush(ctx, el, scope, isLast);
500-
}
501-
}
502-
else {
509+
const paintRect = el.getPaintRect();
510+
if (!repaintRect || repaintRect && paintRect.intersect(repaintRect)) {
503511
// If no repaintRect, all displayables need to be painted once
512+
// If there is repaintRect, only render the intersected ones
504513
brush(ctx, el, scope, isLast);
505-
// console.log('brush', el.id);
514+
el.setPrevPaintRect(paintRect);
506515
}
507516
}
508517

src/graphic/Displayable.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ class Displayable<Props extends DisplayableProps = DisplayableProps> extends Ele
132132
protected _rect: BoundingRect
133133
protected _paintRect: BoundingRect
134134
protected _prevPaintRect: BoundingRect
135+
// protected _prevPaintRectPending: BoundingRect // waiting to replace _prevPaintRect in frame rendering stage
136+
// protected _prevPaintRectFlushed: boolean
135137

136138
/************* Properties will be inejected in other modules. *******************/
137139

@@ -251,8 +253,13 @@ class Displayable<Props extends DisplayableProps = DisplayableProps> extends Ele
251253
}
252254

253255
setPrevPaintRect(paintRect: BoundingRect) {
254-
this._prevPaintRect = new BoundingRect(0, 0, 0, 0);
255-
this._prevPaintRect.copy(paintRect);
256+
if (paintRect) {
257+
this._prevPaintRect = new BoundingRect(0, 0, 0, 0);
258+
this._prevPaintRect.copy(paintRect);
259+
}
260+
else {
261+
this._prevPaintRect = null;
262+
}
256263
}
257264

258265
getPrevPaintRect(): BoundingRect {

test/dirty-rect.html

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
var zr = zrender.init(document.getElementById('main'), {
1313
});
1414

15+
var text = new zrender.Text({
16+
style: {
17+
fill: 'white',
18+
text: 'Start'
19+
},
20+
zlevel: 1
21+
});
1522
var circle = new zrender.Circle({
1623
scale: [1, 1],
1724
shape: {
@@ -20,16 +27,32 @@
2027
r: 50
2128
},
2229
style: {
23-
}
30+
fill: '#f0f',
31+
opacity: 0.8
32+
},
33+
textContent: text,
34+
textConfig: {
35+
position: 'inside'
36+
},
37+
zlevel: 1
2438
});
2539
zr.add(circle);
2640

41+
text.animateTo({
42+
style: {
43+
fill: 'yellow'
44+
}
45+
}, {
46+
duration: 3000
47+
});
48+
2749
circle.animateTo({
2850
shape: {
51+
cx: 800,
2952
cy: 200
3053
}
3154
}, {
32-
duration: 1000
55+
duration: 3000
3356
});
3457

3558
var rects = [];
@@ -89,15 +112,14 @@
89112
});
90113
zr.add(a);
91114

92-
setTimeout(() => {
115+
setTimeout(function () {
93116
zr.remove(a);
117+
}, 5000);
118+
setInterval(function () {
119+
for (var i = 0; i < 2; ++i) {
120+
rects[i].attr('position', [800 * Math.random(), 400 * Math.random()]);
121+
}
94122
}, 3000);
95-
// setInterval(() => {
96-
// for (var i = 0; i < 2; ++i) {
97-
// rects[i].attr('position', [800 * Math.random(), 400 * Math.random()]);
98-
// }
99-
100-
// }, 3000);
101123
</script>
102124

103125
</body>

0 commit comments

Comments
 (0)