Webkit Animation Is Leaving Junk Pixels Behind on the Screen

Webkit animation is leaving junk pixels behind on the screen



The solution

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

You can use the background colour of your box as the box-shadow colour if you feel this is too noticeable.

Alternatively, according to this answer on a similar issue in Chrome (thanks to Sebastian in the comments for the tip-off), you may want to try:

outline: 1px solid transparent;

What's going on?

I've given a fairly lengthy explanation elsewhere, but here's the short version. For performance reasons, WebKit only repaints those part of a page that it thinks might have changed. However, the iOS (pre-7) Safari implementation of border radius anti-aliases a few pixels beyond the calculated dimensions of a box. Since WebKit doesn't know about these pixels, they don't get redrawn; instead, they are left behind and build up on each animation frame.

The usual solution—as I suggested in my other answer—is to force that element to require hardware acceleration so that it gets painted as a separate layer. However, too many small elements or a few large ones will result in a lot of tiles getting pushed to the GPU, with obvious performance implications.

Using box-shadow solves the problem more directly: it extends the repaint dimensions of the box, forcing WebKit to repaint the extra pixels. The known performance implications of box-shadow in mobile browsers are directly related to the blur radius used, so a one pixel shadow should have little-to-no effect.

Webkit render bug on border-radius when using animation

What's happening is the browser is rendering at the very small size, and then handing it as a bitmap to hardware compositing to do the scaling. So you're scaling up the 5px version, and hence the effect you're seeing.

You can solve this by doing it the other way around - begin by rendering at the large size...

li{
width: 60px;
height: 60px;

and scaling down...

  <li style="transform: rotate(0deg) translateY(-35vh) scale(calc(1/12));">
<span></span>
</li>
<li style="transform: rotate(20deg) translateY(-35vh) scale(calc(1/12));">
<span></span>
</li>

Here's the entire code - you can run it inline by clicking "Run code snippet" below. Updated codepen: http://codepen.io/anon/pen/bdzEEv

$('button').click(function(){      $('span').addClass('scaleup').bind('animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd', function (e){     $(this).removeClass(); });});
body{  background: white;}button{  margin: 40px auto;  display: block;  width: 150px;  height: 35px;}ul{  position: relative;  border-radius: 50%;  width: 70vh;  height: 70vh;  margin-top: 15vh;  margin-left: calc(50vw - (70vh)/2);  padding: 0;}
li{ position: absolute; top: 50%; left: 50%; width: 60px; height: 60px; border-radius: 50%; background-color: red; list-style: none; -webkit-backface-visibility: hidden;}span{ display: block; width: 100%; height: 100%; border-radius: 50%; background-color: black;}li span.scaleup{ animation: scaleup 2s 1;}
@keyframes scaleup{ 10%{ transform: scale(12); } 100%{ transform: scale(1); }}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><button>Toggle</button><ul>  <li style="transform: rotate(0deg) translateY(-35vh) scale(calc(1/12));">    <span></span>  </li>  <li style="transform: rotate(20deg) translateY(-35vh) scale(calc(1/12));">    <span></span>  </li><ul>

How can I avoid animation artifacts on my touch-draggable border-radius element on iPad?



The fix

Add this rule to your #bawl { … } ruleset:

-webkit-transform: translateZ(0);

(If you need to avoid hardware acceleration, you can use outline: 1px solid transparent—for more details, see my answer to a similar question.)

This removes the trailing artefacts, but why? It's a combination of Quartz (the drawing engine in iOS) not clipping anti-aliased lines to the edges of a shape and how WebKit repaints a web page after part of it changes.

Drawing a webpage

First, a quick and (over)simplified run down of how WebKit gets from a tree of DOM nodes to a bitmap image representing the rendered webpage.

You already know that a webpage gets parsed into a tree of elements, called the DOM. Each node in the DOM is presented in a certain way depending on the styles applied to it. It is possible for nodes to overlap, or be overlapped by, other nodes depending on things like z-order, transparency, overflow, positioning etc.

This is broadly similar to how things work under the hood. WebKit maps each DOM node to a corresponding RenderObject, which has all the information it needs to paint an individual DOM node. Each RenderObject is mapped to either its own or an ancestor's RenderLayer, which is a conceptual way of dealing with how nodes are "layered"—i.e. painted over or under other nodes.

To render a webpage, each RenderLayer is painted from back-to-front by z-order. Children behind that layer painted are first, followed by the layer itself, followed by children in front of it. To paint itself, the RenderLayer calls the paint method on its corresponding RenderObjects.

WebKit has two code paths for rendering a given RenderLayer: the software path and the hardware accelerated path. The software path paints each RenderObject directly to the image of the rendered webpage that you see in your browser, whereas the hardware path allows certain RenderLayers to be designated as compositing layers, which means that it and its children are drawn separately and finally composited into a single image by the GPU. The hardware path is used whenever one or more of the RenderLayers on a page require hardware acceleration, or when it is explicitly required by a flag in the browser (e.g. Chrome).

Updating part of a webpage

When an animation or some other event changes how part of a page looks, you don't want to redraw the entire webpage. Instead, WebKit draws a box around the changed area—the damage rect—and only redraws bits of each RenderLayer that overlap with that box. A RenderLayer that doesn't overlap the damage rect is skipped entirely.

If you're rendering via the software path, WebKit directly recomposes the damage rect onto the bitmap image of the full rendered page, leaving the rest of the image unchanged. The hardware path, however, repaints each compositing layer separately and recomposites them into a new image.

What is going wrong

The translateZ fix applies a 3D transform to the element. This forces the RenderLayer that paints the ball to require hardware acceleration, meaning that WebKit will use the hardware path instead of the software path. This implies that the problem is related to using the software path.

When the ball is painted, the border radius means that the edges are anti-aliased. Because of a known issue related to Quartz, the edges of the shape are anti-aliased such that some pixels fall outside of the damage rect calculated when you change the ball's position on the page. Using the software path, the browser redraws only the changed region of the rendered page image, leaving the rest of the image untouched. The semi-transparent pixels that fall outside of this area won't be updated, which accounts for the artefacts left behind when you move the ball.

By contrast, the hardware path redraws that layer (and its child layers) separately then recomposites the page. No "ghost pixels" are left over from the last time that layer was rendered.

TL;DR

When WebKit uses the software rendering path, changes to part of a page are made directly to the image representing the whole page. For performance reasons, WebKit only redraws those parts of an image that have changed. However, when Quartz draws a rounded rectangle, it anti-aliases the edges such that some pixels fall outside the region that WebKit knows to redraw. The fix is to force that layer to require hardware acceleration by applying a 3D transform, meaning that element is drawn separately from the rest of the page and recomposed after.

JQuery UI draggable element leaves weird traces in Chrome

Finally I have a working workaround .

Just using
outline: 1px solid transparent;
on the div fixes the problem :)

It's an anti-aliasing issue.

Fixed attachment background image flicker/disappear in chrome when coupled with a css transform

This has been a very common unsolved mystery. Recently I had the same problem, and '-webkit-backface-visibility: hidden', proved to be less than useless (on my 'fixed' attached background), since the background just disappeared when it was set. (Additional Info: the reason is that when the background is set as fixed, it is almost similar to putting a fixed 'div' in the background and setting the original div background to be transparent. Hidden backface does the obvious).

To solve the current problem, try setting the 'position' propery of the element as 'static', or if you have given it some other value, namely 'relative', 'fixed' or 'absolute', just remove those.

If you don't remember setting the position property, and the problem still persist, my suggestion is that you use a debugging tool on chrome or firefox, to

make sure there are no manually set values to the 'position' property other than
'static'.

Just spent half an hour searching... Thought this could make it easier for you... regards. :)

Moving animation off screen with scroll view

Fixed this, changing bottom animation to origin.X does the trick

Transitions on the CSS display property

You can concatenate two transitions or more, and visibility is what comes handy this time.