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