如何避免iPad上触摸拖动的圆角元素出现动画伪影?

8

我写了一个小示例页面,其中的元素可以在触摸设备上拖动(基于Peter-Paul Koch的代码)。

我有两个可拖动的<div>元素:一个绿色块和一个红色球(通过border-radius制作成球形)。拖动是通过使用ontouchstartontouchmoveontouchend实现的,并且“动画”是通过改变这些元素的topleft CSS属性来完成的。

当我拖动绿色块时,一切都很顺利。

但是,在iPad 1(运行iOS 5.1.1)或iPad 3(6.0.1)的Safari中拖动红色块时,会在圆形后缘留下小的浅红色轨迹(请参见下面的截屏)。

然而,在我的iPhone 5(6.1.4)上看不到这些轨迹。

Artifacts on my element that uses border-radius

有没有办法消除这些轨迹?

(附加问题:这种效果有术语吗?“Ghosting”?“Artifacts”?)


1
我认为这个问题的正确术语是“iOS渲染错误”。 - Pointy
1
我对这里发生的事情有很强的怀疑。首先,进行一个测试:您能否尝试将-webkit-transform: translateZ(0);添加到#bawl { ... }规则集中? - Jordan Gray
1
太好了!这本身是一个基本的技巧,但我对正在发生的事情有一种相当冗长的怀疑。我会在午餐时加上一个带有解释的答案。 :) - Jordan Gray
2
至少在iOS 7 beta 3中,您的网站按预期工作,没有任何伪像。 - luk2302
1
@JordanGray:一点也不居高临下,在Stack Overflow上这并不重要——答案旨在为未来的访问者提供帮助,因此添加细节/来源/参考总是有益的。 - Paul D. Waite
显示剩余7条评论
2个回答

22

解决方法

将以下规则添加到您的#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变换来强制该层需要硬件加速,这意味着该元素将单独绘制并在之后重新组合到页面中。

1
一个小细节:compositing layers是代码吗?如果不是,就不应该用反引号标记。 - Paul D. Waite
1
@PaulD.Waite 哦,发现得好!我在那些反引号上太急了。改成<dfn>。 :) - Jordan Gray

0

在所有 WebKit 浏览器(移动和桌面)上都遇到了类似的问题。

我清除了缓存和其他一切,之后错误就再也没有出现过。 而且似乎只有我能看到它。我在这里发布了它,但因为没有人遇到这个问题,所以得到了很多负评...

不过,我会尝试一下,说不定会奏效。


我不明白你的意思。你认为来自站点其他位置、不在问题页面上的缓存文件引起了问题? - Paul D. Waite
无论如何,为了参考,我已经在我的iOS 5 iPad 1上清除了Safari的缓存(设置> Safari>清除Cookie和数据),但问题仍然存在。 - Paul D. Waite
不,问题在于谷歌浏览器以一种非常特殊的方式呈现了我的页面。除了整个页面上的彩色噪点之外,一切看起来都很好。这对我来说看起来相当奇怪,所以我在这里问了一下。然而,在清除缓存后,它再也没有出现过...你的设备运行哪个版本? - David Fariña
我在我的iPad 2版本5.1和iPhone5 6.1.4上尝试过,还有iPad 2模拟器6.1和iPad 3模拟器也是6.1。即使在比你使用的版本更旧的iPad上也无法复现。看起来这是一个非常奇怪的问题。 - David Fariña
哦,有趣,它在你的iPad 2上没有显示出来。我将尝试在我这里可以访问的iPad 3上清除缓存并重新测试。 - Paul D. Waite
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接