Skip to content

Commit 802cf7d

Browse files
authored
fix: element z-index sorting rendering abnormality (#1921)
1 parent c9905e3 commit 802cf7d

File tree

7 files changed

+153
-48
lines changed

7 files changed

+153
-48
lines changed

.changeset/sour-dogs-fly.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@antv/g-plugin-canvaskit-renderer': patch
3+
'@antv/g-plugin-canvas-renderer': patch
4+
'@antv/g-lite': patch
5+
---
6+
7+
fix: element z-index sorting rendering abnormality

__tests__/demos/bugfix/1910.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Canvas, Text, CanvasEvent } from '@antv/g';
2+
import { Renderer } from '@antv/g-canvas';
3+
4+
// const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
5+
// const randomLetter = () => letters.charAt(Math.floor(Math.random() * 26));
6+
7+
/**
8+
* @see https://github.com/antvis/G/issues/1910
9+
*/
10+
export async function issue_1910(context: { canvas: Canvas }) {
11+
const { canvas } = context;
12+
console.log(canvas);
13+
14+
canvas.setRenderer(
15+
new Renderer({
16+
enableAutoRendering: false,
17+
}),
18+
);
19+
20+
const canvasDom = canvas
21+
.getContextService()
22+
.getDomElement() as unknown as HTMLCanvasElement;
23+
const btnDom = document.createElement('button');
24+
btnDom.style.cssText =
25+
'position: absolute; top: 0px; left: 600px; width: 120px; height: 32px';
26+
btnDom.textContent = 'trigger render';
27+
28+
canvasDom.parentElement.appendChild(btnDom);
29+
30+
const text1 = new Text({
31+
style: {
32+
x: 100,
33+
y: 200,
34+
text: 'A',
35+
fontSize: 16,
36+
fill: '#f00',
37+
zIndex: 1, // zIndex 为 0 或不配置时,一切正常
38+
},
39+
});
40+
41+
const text2 = new Text({
42+
style: {
43+
x: 100,
44+
y: 300,
45+
text: 'B',
46+
fontSize: 16,
47+
fill: '#000',
48+
},
49+
});
50+
51+
canvas.addEventListener(CanvasEvent.READY, () => {
52+
canvas.appendChild(text1);
53+
canvas.appendChild(text2);
54+
canvas.render();
55+
56+
btnDom.onclick = () => {
57+
canvas.removeChild(text1);
58+
canvas.removeChild(text2);
59+
console.log('removed');
60+
61+
canvas.render(); // 添加这行代码后不会出现锯齿,但会有图元丢失问题
62+
63+
// text1.style.text = randomLetter();
64+
text1.style.zIndex = Math.round(Math.random());
65+
// text2.style.text = randomLetter();
66+
67+
canvas.appendChild(text1);
68+
canvas.appendChild(text2);
69+
console.log('appended');
70+
71+
canvas.render();
72+
};
73+
});
74+
}

__tests__/demos/bugfix/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export { issue_1760 } from './1760';
1111
export { issue_1176 } from './1176';
1212
export { issue_1882 } from './1882';
1313
export { issue_1906 } from './1906';
14+
export { issue_1910 } from './1910';
1415
export { issue_1911 } from './1911';
1516
export { textWordWrap } from './textWordWrap';
1617
export { group_with_stroke } from './group-with-stroke';

packages/g-lite/src/services/RenderingService.ts

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,10 @@ export class RenderingService {
274274
internalRenderSingleDisplayObject(currentObject);
275275

276276
// recursive rendering its children
277-
const objects = currentObject.sortable.sorted || currentObject.childNodes;
277+
const objects =
278+
currentObject.sortable?.sorted?.length > 0
279+
? currentObject.sortable.sorted
280+
: currentObject.childNodes;
278281
for (let i = objects.length - 1; i >= 0; i--) {
279282
stack.push(objects[i] as unknown as DisplayObject);
280283
}
@@ -288,26 +291,38 @@ export class RenderingService {
288291
) {
289292
// avoid re-sorting the whole children list
290293
sortable.dirtyChildren.forEach((child) => {
294+
// remove from sorted list
295+
const sortIndex = sortable.sorted.indexOf(child);
296+
if (sortIndex > -1) {
297+
sortable.sorted.splice(sortIndex, 1);
298+
}
299+
291300
const index = displayObject.childNodes.indexOf(child as IChildNode);
292-
if (index === -1) {
293-
// remove from sorted list
294-
const index = sortable.sorted.indexOf(child);
295-
if (index >= 0) {
296-
sortable.sorted.splice(index, 1);
301+
if (index > -1) {
302+
if (sortable.sorted.length === 0) {
303+
sortable.sorted.push(child);
304+
} else {
305+
const index = sortedIndex(
306+
sortable.sorted as IElement[],
307+
child as IElement,
308+
);
309+
sortable.sorted.splice(index, 0, child);
297310
}
298-
} else if (sortable.sorted.length === 0) {
299-
sortable.sorted.push(child);
300-
} else {
301-
const index = sortedIndex(
302-
sortable.sorted as IElement[],
303-
child as IElement,
304-
);
305-
sortable.sorted.splice(index, 0, child);
306311
}
307312
});
308313
} else {
309314
sortable.sorted = displayObject.childNodes.slice().sort(sortByZIndex);
310315
}
316+
317+
// When the child elements are changed and sorted, if there are no more stacked elements in the child elements (i.e. zIndex != 0), clear the sort list
318+
if (
319+
sortable.sorted?.length > 0 &&
320+
displayObject.childNodes.filter(
321+
(child: IElement) => child.parsedStyle.zIndex,
322+
).length === 0
323+
) {
324+
sortable.sorted = [];
325+
}
311326
}
312327

313328
destroy() {

packages/g-lite/src/services/SceneGraphService.ts

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ export class DefaultSceneGraphService implements SceneGraphService {
129129
const { sortable } = parent as unknown as Element;
130130
if (
131131
sortable?.sorted?.length ||
132+
sortable.dirty ||
132133
(child as unknown as Element).parsedStyle.zIndex
133134
) {
134135
if (sortable.dirtyChildren.indexOf(child) === -1) {
@@ -156,42 +157,44 @@ export class DefaultSceneGraphService implements SceneGraphService {
156157
}
157158

158159
detach<C extends INode>(child: C) {
159-
if (child.parentNode) {
160-
const transform = (child as unknown as Element).transformable;
161-
// if (transform) {
162-
// const worldTransform = this.getWorldTransform(child, transform);
163-
// mat4.getScaling(transform.localScale, worldTransform);
164-
// mat4.getTranslation(transform.localPosition, worldTransform);
165-
// mat4.getRotation(transform.localRotation, worldTransform);
166-
// transform.localDirtyFlag = true;
167-
// }
168-
169-
// parent needs re-sort
170-
const { sortable } = child.parentNode as Element;
171-
// if (sortable) {
172-
if (
173-
sortable?.sorted?.length ||
174-
(child as unknown as Element).style?.zIndex
175-
) {
176-
if (sortable.dirtyChildren.indexOf(child) === -1) {
177-
sortable.dirtyChildren.push(child);
178-
}
179-
sortable.dirty = true;
180-
sortable.dirtyReason = SortReason.REMOVED;
181-
}
160+
if (!child.parentNode) {
161+
return;
162+
}
182163

183-
const index = child.parentNode.childNodes.indexOf(
184-
child as unknown as IChildNode & INode,
185-
);
186-
if (index > -1) {
187-
child.parentNode.childNodes.splice(index, 1);
188-
}
164+
const transform = (child as unknown as Element).transformable;
165+
// if (transform) {
166+
// const worldTransform = this.getWorldTransform(child, transform);
167+
// mat4.getScaling(transform.localScale, worldTransform);
168+
// mat4.getTranslation(transform.localPosition, worldTransform);
169+
// mat4.getRotation(transform.localRotation, worldTransform);
170+
// transform.localDirtyFlag = true;
171+
// }
189172

190-
if (transform) {
191-
this.dirtifyWorld(child, transform);
173+
// parent needs re-sort
174+
const { sortable } = child.parentNode as Element;
175+
// if (sortable) {
176+
if (
177+
sortable?.sorted?.length ||
178+
(child as unknown as Element).style?.zIndex
179+
) {
180+
if (sortable.dirtyChildren.indexOf(child) === -1) {
181+
sortable.dirtyChildren.push(child);
192182
}
193-
child.parentNode = null;
183+
sortable.dirty = true;
184+
sortable.dirtyReason = SortReason.REMOVED;
185+
}
186+
187+
const index = child.parentNode.childNodes.indexOf(
188+
child as unknown as IChildNode & INode,
189+
);
190+
if (index > -1) {
191+
child.parentNode.childNodes.splice(index, 1);
192+
}
193+
194+
if (transform) {
195+
this.dirtifyWorld(child, transform);
194196
}
197+
child.parentNode = null;
195198
}
196199

197200
getOrigin(element: INode) {

packages/g-plugin-canvas-renderer/src/CanvasRendererPlugin.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,9 @@ export class CanvasRendererPlugin implements RenderingPlugin {
236236
}
237237

238238
const objects =
239-
currentObject.sortable.sorted || currentObject.childNodes;
239+
currentObject.sortable?.sorted?.length > 0
240+
? currentObject.sortable.sorted
241+
: currentObject.childNodes;
240242
// should account for z-index
241243
for (let i = objects.length - 1; i >= 0; i--) {
242244
stack.push(objects[i] as unknown as DisplayObject);

packages/g-plugin-canvaskit-renderer/src/CanvaskitRendererPlugin.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,10 @@ export class CanvaskitRendererPlugin implements RenderingPlugin {
303303
this.renderDisplayObject(object, canvas);
304304
}
305305

306-
const sorted = object.sortable.sorted || object.childNodes;
306+
const sorted =
307+
object.sortable?.sorted?.length > 0
308+
? object.sortable.sorted
309+
: object.childNodes;
307310

308311
// should account for z-index
309312
sorted.forEach((child: DisplayObject) => {

0 commit comments

Comments
 (0)