【问题标题】:How can I avoid animation artifacts on my touch-draggable border-radius element on iPad?如何避免 iPad 上可触摸拖动的边框半径元素上出现动画伪影?
【发布时间】:2013-07-08 16:14:50
【问题描述】:

我编写了一个小示例页面,其中包含可在触摸设备上拖动的元素(基于 code by 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) 上看不到这些踪迹。

有没有办法摆脱这些痕迹?

(额外问题:这种效果有术语吗?“重影”?“神器”?)

【问题讨论】:

  • 我认为恰当的术语是“iOS 渲染错误”。
  • 我非常怀疑这里发生了什么。首先,一个测试:你可以尝试添加 -webkit-transform: translateZ(0);到#bawl { ... } 规则集?
  • 太棒了!这本身就是一个基本技巧,但我对到底发生了什么有一个相当冗长的怀疑。我会在午餐时添加一个带有解释的答案。 :)
  • 至少在 iOS 7 beta 3 中,您的网站可以正常工作,没有人工痕迹。
  • @JordanGray:一点也不光顾,而且在 Stack Overflow 上这无论如何都无关紧要 - 答案既适用于未来的访问者,也适用于提出问题的用户,所以添加细节/来源/参考总是好的。

标签: javascript ios css touch-event


【解决方案1】:

修复

将此规则添加到您的 #bawl { … } 规则集:

-webkit-transform: translateZ(0);

(如果您需要避免硬件加速,可以使用outline: 1px solid transparent——更多详细信息,请参阅my answer to a similar question。)

这会删除尾随的伪影,但为什么呢?这是Quartz (the drawing engine in iOS) 没有将抗锯齿线剪切到形状边缘以及 WebKit 如何在部分更改后重新绘制网页的组合。

绘制网页

首先,快速(过度)简化地介绍 WebKit 如何从 DOM 节点树获取表示呈现网页的位图图像。

您已经知道网页会被解析为元素树,称为 DOM。 DOM 中的每个节点都以特定的方式呈现,具体取决于应用于它的样式。节点可能与其他节点重叠或被其他节点重叠,具体取决于 z 顺序、透明度、溢出、定位等。

这与引擎盖下的工作方式大体相似。 WebKit 将每个 DOM 节点映射到对应的RenderObject,它包含绘制单个 DOM 节点所需的所有信息。每个RenderObject 都映射到它自己的或祖先的RenderLayer,这是一种处理节点如何“分层”的概念方式——即绘制在其他节点之上或之下。

为了呈现一个网页,每个RenderLayer 按z 顺序从后向前绘制。该图层后面的孩子首先绘制,然后是图层本身,然后是它前面的孩子。为了绘制自己,RenderLayer 在其对应的RenderObjects 上调用paint 方法。

WebKit 有两个代码路径用于呈现给定的RenderLayer:软件路径和硬件加速路径。软件路径将每个RenderObject 直接绘制到您在浏览器中看到的渲染网页的图像上,而硬件路径允许将某些RenderLayers 指定为合成层,这意味着它和它的孩子是分开绘制的,最后由 GPU 合成为一个单一的图像。只要页面上的一个或多个 RenderLayers 需要硬件加速,或者浏览器中的标志明确要求(例如 Chrome),就会使用硬件路径。

更新网页的一部分

当动画或其他一些事件改变了页面部分的外观时,您不希望重绘整个网页。相反,WebKit 在更改区域(损坏区域)周围绘制了一个框,并且仅重绘每个 RenderLayer 与该框重叠的位。完全跳过不与损坏区域重叠的RenderLayer

如果您通过软件路径进行渲染,WebKit 会直接将损坏的矩形重新组合到完整渲染页面的位图图像上,而图像的其余部分保持不变。但是,硬件路径会分别重新绘制每个合成层,并将它们重新合成到一个新图像中。

出了什么问题

translateZ 修复将 3D 变换应用于元素。这会强制绘制球的RenderLayer 需要硬件加速,这意味着 WebKit 将使用硬件路径而不是软件路径。这意味着问题与使用软件路径有关。

当球被绘制时,边界半径意味着边缘被消除锯齿。由于known issue related to Quartz,形状的边缘是抗锯齿的,因此当您更改球在页面上的位置时,一些像素会落在计算的损坏矩形之外。使用软件路径,浏览器仅重绘渲染页面图像的更改区域,而图像的其余部分保持不变。落在此区域之外的半透明像素不会更新,这是您移动球时留下的伪影的原因。

相比之下,硬件路径分别重绘该层(及其子层),然后重新组合页面。上次渲染该层时没有留下任何“幽灵像素”。

TL;DR

当 WebKit 使用软件渲染路径时,对部分页面的更改直接对代表整个页面的图像进行。出于性能原因,WebKit 仅重绘图像中已更改的那些部分。然而,当 Quartz 绘制一个圆角矩形时,它会对边缘进行抗锯齿处理,这样一些像素就会落在 WebKit 知道要重绘的区域之外。解决方法是通过应用 3D 变换来强制该层需要硬件加速,这意味着该元素与页面的其余部分分开绘制并在之后重新组合。

【讨论】:

  • 一个小点:是compositing layers 代码吗?如果不是,则不应使用反引号标记。
  • @PaulD.Waite 哦,不错!我对那些反引号过于渴望了。更改为<dfn>。 :)
【解决方案2】:

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

我清除了缓存和所有内容,之后错误就再也没有发生过。 似乎我也只能看到它。我把它贴在这里并得到了很多反对意见,因为没有人也遇到过这个问题......

不管怎么试,都可以

【讨论】:

  • 我在两种不同的设备上都试过了,所以我不确定清除浏览器的缓存是否可能有效果。 (即使确实如此,我的用户是否打算在第一次访问我的页面后清除他们的缓存?)很抱歉听到您的问题被否决了。
  • 这就是我所说的。其他人看不到问题,因为他们从未在我的网站上。因此,如果新用户访问您的页面,他将不会看到它。顺便说一句:我在装有 iOS 6.1.4 的 iphone 5 上看不到您的问题。也许只是尝试一下,不会伤害你太多
  • 我不明白你的意思。您认为问题页面上不在的站点上其他地方的缓存文件导致了问题?
  • Anyhoo:作为参考,我已经清除了我的 iOS 5 iPad 1 上的 Safari 缓存(设置 > Safari > 清除 Cookie 和数据),问题仍然存在。
  • 不,问题是 google chrome 以一种非常特殊的方式呈现了我的页面。一切看起来都不错,除了页面上到处都是五颜六色的噪音。我觉得很奇怪,就在这里问。然而,在清除缓存后,它再也没有出现过……您的设备运行的是哪个版本?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-10
  • 2012-06-11
  • 1970-01-01
  • 1970-01-01
  • 2022-01-23
  • 1970-01-01
相关资源
最近更新 更多