Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

设置 zIndex 时,重复渲染 Text 图元存在变粗和锯齿现象 #1910

Open
zhaosiyi opened this issue Mar 5, 2025 · 5 comments
Open
Labels

Comments

@zhaosiyi
Copy link

zhaosiyi commented Mar 5, 2025

问题描述

  1. 使用 canvas 渲染器创建画布,并设置 enableAutoRendering: false 手动渲染。
  2. 创建 Text 图元,并为其设置非 0 的 zIndex 。
  3. 将 Text 图元添加到画布中执行渲染,可以看到初次渲染结果正常。
  4. 将 Text 从画布中移除,并重新添加到画布中执行渲染,重复多次。可以看到文字逐渐变粗,出现锯齿现象。

复现地址:https://stackblitz.com/edit/react-j9yrujub?file=index.js,index.html

多次点击按钮即可观察到文字逐渐模糊的现象。

不设置或者将 zIndex 设置为 0 时,一切正常。
若不重新挂载元素,直接连续渲染数次,也没有异常。

svg 渲染器下没有这个问题。

推测可能是卸载并重新挂载图元后,渲染器重复渲染了文字,导致文字变粗和锯齿的出现?

测试代码

import { Canvas, CanvasEvent, Text } from '@antv/g';
import { Renderer as CanvasRenderer } from '@antv/g-canvas';
import { Renderer as SVGRenderer } from '@antv/g-svg';

// create a renderer
const canvasRenderer = new CanvasRenderer({
  enableAutoRendering: false,
});
const svgRenderer = new SVGRenderer({
  enableAutoRendering: false,
});

// create a canvas
const canvas = new Canvas({
  container: 'container',
  width: 600,
  height: 500,
  renderer: canvasRenderer,
});

const text1 = new Text({
  style: {
    x: 100,
    y: 200,
    text: 'A',
    fontSize: 16,
    fill: '#000',
    zIndex: 1, // zIndex 为 0 或不配置时,一切正常
  },
});

const text2 = new Text({
  style: {
    x: 100,
    y: 300,
    text: 'A',
    fontSize: 16,
    fill: '#000',
  },
});

canvas.addEventListener(CanvasEvent.READY, () => {
  canvas.appendChild(text1);
  canvas.appendChild(text2);
  canvas.render();

  document.querySelector('#render').onclick = () => {
    canvas.removeChild(text1);
    canvas.removeChild(text2);

    // canvas.render(); // 添加这行代码后不会出现锯齿,但会有图元丢失问题

    canvas.appendChild(text1);
    canvas.appendChild(text2);

    canvas.render();
  };
});
<div id="container" />
<button id="render">render</button>

环境信息

  • @antv/g: 6.1.21
  • @antv/g-canvas: 2.0.40
@zhaosiyi zhaosiyi added the bug label Mar 5, 2025
Copy link

您提到的问题是关于在使用 canvas 渲染器时,设置 zIndex 后重复渲染 Text 图元会导致文字变粗和出现锯齿的现象。根据您的描述,这个问题在 svg 渲染器下不存在,并且在不设置 zIndex 或将其设置为 0 时也不会出现。

这个问题可能与 canvas 渲染器在处理 zIndex 时的渲染逻辑有关,尤其是在手动控制渲染的情况下。由于 canvas 渲染器可能在卸载并重新挂载图元后重复渲染了文字,导致了这种现象。

可能的解决方案:

  1. 在每次重新挂载元素后,确保清除画布:在重新挂载元素之前,尝试清除画布以避免重复渲染导致的叠加效果。

  2. 检查渲染逻辑:确保在每次渲染时,画布的状态是干净的,避免残留的渲染状态影响新的渲染。

  3. 使用 canvas.render() 清除画布:在移除元素后,调用 canvas.render() 清除画布,然后再重新添加元素并渲染。

如果这些方法仍然无法解决问题,可能需要进一步查看 @antv/g-canvas 的实现细节,或者考虑提交一个 issue 给开发团队以获得更深入的支持。

如果您需要,我可以帮助您在 GitHub 上创建一个 issue 来报告这个问题。请告诉我您的决定。

🪧 Tips For further assistance, please describe your question in the comments and @petercat-assistant to start a conversation with me.

@wang1212
Copy link
Member

能提供两张对比的截图吗?根据提供的示例来看,反馈的问题不是很明显

@sz-p
Copy link

sz-p commented Mar 14, 2025

单次渲染

Image

多次渲染

Image

对比效果还是有些明显的

Debug一下RenderingService.ts会更加明显:

首次:

Image

第二次:

Image

每次会递增两个test元素。

@zhaosiyi
Copy link
Author

在字体较小的时候观察起来比较明显,

Image

Image

进一步排查可以看到 renderListCurrentFrame 中 Text 重复的原因是每次重新挂载元素后 Group 元素的 sortable.sorted 内 Text 图元重复。

Image

看起来 sortable.sort 的计算逻辑应该在这里的 sort() 函数内:

private sort(displayObject: DisplayObject, sortable: Sortable) {

再往下看可以看到重复添加元素的代码位置应该在这里,按 zIndex 计算完 Text 图元的下标位置后,直接把它插入到了对应位置,没有考虑重复的问题,加上一段去重逻辑应该可以暂时解决问题。

Image

Image

我感觉问题的原因是在 removeChild() 之后,Text 图元仍在 sorted 列表里面,随后重新 appendChild() 的时候,Text 被重新标记了 dirty,然后走了上面那一段计算 index 然后 splice() 插入的逻辑,导致了重复。

目前我对这部分渲染器的逻辑还不是很了解,可能存在其他从根本上避免重复的方式,比如在 removeChild() 的时候把元素从 sorted 中移除之类的?可以帮忙再看下吗? @wang1212

@wang1212
Copy link
Member

wang1212 commented Mar 15, 2025

在字体较小的时候观察起来比较明显,

Image

Image

进一步排查可以看到 renderListCurrentFrame 中 Text 重复的原因是每次重新挂载元素后 Group 元素的 sortable.sorted 内 Text 图元重复。

Image

看起来 sortable.sort 的计算逻辑应该在这里的 sort() 函数内:

private sort(displayObject: DisplayObject, sortable: Sortable) {

再往下看可以看到重复添加元素的代码位置应该在这里,按 zIndex 计算完 Text 图元的下标位置后,直接把它插入到了对应位置,没有考虑重复的问题,加上一段去重逻辑应该可以暂时解决问题。

Image

Image

我感觉问题的原因是在 removeChild() 之后,Text 图元仍在 sorted 列表里面,随后重新 appendChild() 的时候,Text 被重新标记了 dirty,然后走了上面那一段计算 index 然后 splice() 插入的逻辑,导致了重复。

目前我对这部分渲染器的逻辑还不是很了解,可能存在其他从根本上避免重复的方式,比如在 removeChild() 的时候把元素从 sorted 中移除之类的?可以帮忙再看下吗? @wang1212

ok,我来看看


确实是元素被移除时的逻辑有问题,目前的实现代码和元素被添加时逻辑一致,不符合预期。

if (
sortable?.sorted?.length ||
(child as unknown as Element).style?.zIndex
) {
if (sortable.dirtyChildren.indexOf(child) === -1) {
sortable.dirtyChildren.push(child);
}
sortable.dirty = true;
sortable.dirtyReason = SortReason.REMOVED;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants