修复
将此规则添加到您的#bawl { … }
规则集中:
-webkit-transform: translateZ(0);
(如果您需要避免硬件加速,可以使用outline: 1px solid transparent
— 更多详细信息,请参阅我对类似问题的回答。)
这会删除尾随的人工制品,但为什么呢?它结合了Quartz(iOS 中的绘图引擎)不将抗锯齿线条剪切到形状边缘以及 WebKit 如何在部分更改后重新绘制网页。
绘制网页
首先,快速和(过度)简化地介绍 WebKit 如何从 DOM 节点树到表示呈现网页的位图图像。
您已经知道网页被解析为元素树,称为 DOM。DOM 中的每个节点都以特定的方式呈现,具体取决于应用于它的样式。节点可能与其他节点重叠或被其他节点重叠,具体取决于 z 顺序、透明度、溢出、定位等。
这与引擎盖下的工作方式大体相似。WebKit 将每个 DOM 节点映射到一个对应的 DOM 节点RenderObject
,该节点具有绘制单个 DOM 节点所需的所有信息。每个RenderObject
都映射到它自己的或祖先的RenderLayer
,这是处理节点如何“分层”的概念方式 - 即绘制在其他节点之上或之下。
为了呈现网页,每个网页都RenderLayer
按 z 顺序从后向前绘制。该图层后面的孩子首先绘制,然后是图层本身,然后是它前面的孩子。为了绘制自身,RenderLayer
调用它对应RenderObject
的 s 上的 paint 方法。
WebKit 有两个用于渲染给定RenderLayer
的代码路径:软件路径和硬件加速路径。软件路径将每个RenderObject
直接绘制到您在浏览器中看到的渲染网页的图像上,而硬件路径允许RenderLayer
将某些 s 指定为合成层,这意味着它和它的子级分别绘制并最终合成为一个GPU 生成的图像。RenderLayer
只要页面上的一个或多个 s 需要硬件加速,或者浏览器中的标志(例如 Chrome)明确要求它,就会使用硬件路径。
更新网页的一部分
当动画或其他事件改变页面的一部分时,您不想重绘整个网页。相反,WebKit 在更改区域(损坏区域)周围绘制一个框,并且只重绘RenderLayer
与该框重叠的每个部分。RenderLayer
不与伤害矩形重叠的A将被完全跳过。
如果您通过软件路径进行渲染,WebKit 会直接将损坏的矩形重新组合到完整渲染页面的位图图像上,而图像的其余部分保持不变。但是,硬件路径会分别重新绘制每个合成层并将它们重新合成为新图像。
出了什么问题
该translateZ
修复将 3D 变换应用于元素。这迫使RenderLayer
绘制球需要硬件加速,这意味着 WebKit 将使用硬件路径而不是软件路径。这意味着问题与使用软件路径有关。
当球被绘制时,边界半径意味着边缘是抗锯齿的。由于与 Quartz 相关的一个已知问题,形状的边缘是抗锯齿的,因此当您更改球在页面上的位置时,一些像素会落在计算的损坏矩形之外。使用软件路径,浏览器仅重绘渲染页面图像的更改区域,而图像的其余部分保持不变。落在此区域之外的半透明像素不会被更新,这是您移动球时留下的伪影的原因。
相比之下,硬件路径分别重绘该层(及其子层),然后重新组合页面。上次渲染该层时没有留下任何“幽灵像素”。
TL;博士
当 WebKit 使用软件渲染路径时,对部分页面的更改直接对代表整个页面的图像进行。出于性能原因,WebKit 仅重绘图像中已更改的那些部分。然而,当 Quartz 绘制一个圆角矩形时,它会对边缘进行抗锯齿处理,这样一些像素就会落在 WebKit 知道要重绘的区域之外。解决方法是通过应用 3D 变换强制该层需要硬件加速,这意味着该元素与页面的其余部分分开绘制并在之后重新组合。