Webkit动画在屏幕上留下了垃圾像素。

38
以下代码在屏幕上放置了一个白色的框。如果您在iPad上运行此代码(您也可以调整像素以在iPhone上运行它),当您触摸框时,它会从屏幕上滑走,并在其底部边缘留下一条白色像素的痕迹。
<!DOCTYPE HTML>
<html>
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-height, user-scalable=no, maximum-scale=1, minimum-scale=1" />
    <title>Line Bug Demo</title>
    <style>
body {
  background: black;
}
.panel {
  background: white;
  position: absolute;
  z-index: 2;
  width: 1000px;
  height: 500px;
  top: 34px;
  left: 12px;
  -webkit-border-radius: 20px;
  -webkit-transition: left 0.333s ease-in-out;
}
.panel.hide {
  left: -1000px;
}
    </style>
  </head>
  <body>
    <div class="panel" onclick="this.setAttribute('class', 'panel hide')"></div>
  </body>
</html>

获取这个问题的关键是使用边框半径以及进行动画。如果你只是将其从屏幕上弹出,就没有轨迹。如果没有边框半径,则没有轨迹。

以下是我目前发现的解决方法:

.panel.hide { -webkit-border-radius: 0; }

这个方法不太实用,而且不美观。因为我需要对面板进行进出动画,并且希望在屏幕上显示时保留圆角效果。

另一个:

.panel { -webkit-transform: translateZ(0); }

这将把面板放入硬件管线中,从而正确地进行合成。虽然这在简单的演示中可以工作,但在我的真实网络应用程序中使用硬件管线会导致内存不足错误。(非常严重、巨大、立即的类型。)

还有其他什么办法可以消除这个痕迹吗?


4
你尝试过使用-webkit-backface-visibility: hidden;吗? - alex
2
当然是个bug。无论你如何制作动画,都不重要。 - Joshua Smith
2
这是一个有点老的问题,但是我的答案在这里解释了原因。简而言之,Quartz抗锯齿会绘制一行微透明像素,位于元素计算高度和宽度之外,因此它们永远不会在重绘时清除,并且会在每个动画帧上累积。如果您想要,我可以建议一个简单的修复方法,不依赖于硬件加速 - Jordan Gray
3
@jesmith 太好了!我的想法很简单,就是通过在每个方向上将盒子的计算尺寸增加一个像素来强制 WebKit 重新绘制多余的像素。不幸的是,我没有旧的 iOS 设备用于测试,所以你能否为我尝试几个建议?第一个建议:在 .panel 的规则集中添加 box-shadow: 0 0 1px rgba(0,0,0,.05); 。视觉影响应该很小,但它会强制每个边缘都多绘制一行像素。(如果这不起作用,请尝试稍微增加alpha值。) - Jordan Gray
1
@JordanGray,非常有效。我很感激。 - myjobistobehappy
显示剩余12条评论
3个回答

66

解决方案

box-shadow: 0 0 1px rgba(0, 0, 0, 0.05);

如果你觉得box-shadow太明显了,你可以将盒子的背景颜色作为box-shadow的颜色。

或者,根据这个类似Chrome中出现问题的答案(感谢评论中的Sebastian提供的提示),你可以尝试:

outline: 1px solid transparent;

发生了什么?

我在其他地方给出了相当冗长的说明,但这里是简短的版本。由于性能原因,WebKit只会重新绘制页面中它认为可能已更改的那部分内容。然而,iOS(7版之前)Safari实现圆角时会将几个像素反锯齿处理到盒子的计算尺寸之外。由于WebKit不知道这些像素,它们不会被重新绘制;而是留下并在每个动画帧上积累。

通常解决方法-就像我在我的另一个答案中建议的那样-是强制该元素需要硬件加速,以便它作为单独的层进行绘制。然而,太多的小元素或少数大元素将导致大量的图块被推送到GPU中,显然会影响性能。

使用box-shadow可以更直接地解决问题:它扩展了盒子的重绘尺寸,迫使WebKit重新绘制额外的像素。在移动浏览器中已知box-shadow的性能影响与使用的模糊半径直接相关,因此一个像素的阴影应该没有或很少有影响。


2
这个可行。我没有看到性能下降。请注意,在追踪您答案中的链接时,我了解到iOS 7据称已经修复了潜在的quartz错误。 - Joshua Smith
@jesmith 我在另一个问题的评论中看到有人也这样观察过,但我找不到好的参考链接——如果你有的话,我会非常感激!顺便说一下,我深入研究了一下WebKit源代码,并阅读了关于Quartz抗锯齿的内容,以便进一步缩小范围,但我还没有达到能够自信和准确地阐述的程度。 - Jordan Gray
3
我已经使用了盒阴影,但另一个技巧帮了我大忙:感谢这篇文章:https://dev59.com/7mct5IYBdhLWcg3wF5tF#12352196 我正在使用 outline: 1px solid transparent;,效果非常好! - Sebastian
1
@Sebastian 很棒,而且更简洁 - 感谢您的提示!我考虑过 outline 可能会起作用,但从未测试过。也许我应该更新我的答案,链接到那个解决方案。 - Jordan Gray
1
哦,我的天。这太棒了。谢谢! - Cari
显示剩余5条评论

8

我会做的:

  • -webkit-backface-visibility: hidden
  • 使用 -webkit-transform:translateX(left value here) // 或 translate-3d(x,y,z) 动画效果, left 应该被禁用 [*]

确保检查启用父级硬件加速是否有任何不同。

还有一些简单的方法可以强制重绘 - 如果您需要了解有关它们的信息,请告诉我。

[*] 依靠 3D 转换是一种 hack,应谨慎使用,并且在 GPU 和内存之间进行权衡,在某些情况下可能会导致动画卡顿或内存问题(考虑移动设备,在大区域上强制使用 GPU 加速)。

CSS 的 will-change 属性将是标记可以提前优化的属性的正确位置。


"-webkit-backface-visibility: hidden" 非常好用!非常感谢。 - iMoses
2
多亏了这个,我从朋友那里赚到了一杯芒果奶茶。哈哈! - Wesley Brian Lachenal

2

对我来说,这个问题解决了,通过添加以下内容:

will-change: transform;

针对任何要移动的元素,您可以对其CSS进行修改。您可以将transform更改为任何其他需要更改的属性。

其他答案可能如下:

outline: 1px solid transparent;

似乎也能解决问题,但在我的情况下,我必须将大纲增加到50像素,因为它留下了很多内容。如果你发现自己需要这样做,那么也许我的第一个答案可以解决你的问题。在这样做之后,对我来说,轮廓不再是必要的。


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