HTML + CSS infinite scrolling background: Flicker on Safari at repeat

Viewed 553

I'm creating a scene with a bunch of scrolling layers (foreground, midground, background etc...) but annoyingly I get a flicker on Safari (14.0.3) when the animation restarts. This doesn't occur on Chrome or Firefox.

I've created a minimum reproducible example here:

https://brendon.github.io/safari_flicker/index.html

Here's the code:

.animation {
  position: relative;
  height: 395px;
  background-image: linear-gradient(#1b9dd9, #00b6ed 44%, #ffe56c 75%);
}

.animation .scrollingAnimation {
  position: absolute;
  overflow: hidden;
  height: 100%;
  width: 100%;
}

.animation .scrollingAnimation:before {
  content: "";
  position: absolute;
  height: 100%;
  width: 200%;
}

.animation .foreground:before {
  /* Dimensions: */
  /* width: 1696px; */
  /* height: 74px; */
  min-width: 6784px;
  background-image: url("https://brendon.github.io/safari_flicker/foreground.png");
  background-position: left bottom -11px;
  background-repeat: repeat-x;
  background-size: auto 74px;
  transform: translateX(-1696px);
  animation: foreground 10s linear infinite;
}

@keyframes foreground {
  0% {
    transform: translateX(-1696px);
  }
  to {
    transform: translateX(-3392px);
  }
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>

  <div class="animation">
    <div class="foreground scrollingAnimation"></div>
  </div>

</body>

</html>

Here is a video of the issue:

https://github.com/brendon/safari_flicker/raw/main/flicker_video.mp4

I've tried many things to get rid of the issue. It seems to sometimes go away depending on the window width, but I'm looking for a solid solution :D

The issue also exists on iOS Safari.

I should mention that I don't want to animate the background-position property as this causes performance problems and isn't accelerated by the GPU.

2 Answers

Have you thought about using 2 elments with the same image and animation, and offsetting - using delay - the first elements animation by -duration / 2 ?

The idea being that at all times there's one of them on screen and any render delay shouldn't be visible.

See below, I'm animating two pseudo elements.

html, body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
}

.animation, .foreground {
  height: 100%;
  width: 100%;
  background: black;
}

.foreground:before, .foreground:after {
  height: 100%;
  width: 200%;
  
  position: absolute;
  top: 0;
  
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 50vmin;
}

.foreground {
  position: relative;
  overflow-x: hidden;
}

.foreground:before {
  content: 'A';
  background: red;
  
  animation: 10s linear -5s infinite foreground;
}

.foreground:after {
  content: 'B';
  background: blue;
  
  animation: 10s linear 0s infinite foreground;
}

@keyframes foreground {
  0% {
    transform: translateX(100%);
  }
  to {
    transform: translateX(-100%);
  }
}
<div class="animation">
    <div class="foreground scrollingAnimation"></div>
  </div>

I ended up using GSAP fromTo() to manage the transition work instead of relying on the CSS animation:

<div class="foreground scrollingAnimation"><div></div></div>
gsap.fromTo(
  '.foreground > div',
  { xPercent: -25 },
  { xPercent: -50, duration: 10, repeat: -1, ease: 'none' }
)
.scrollingAnimation {
  position: absolute;
  overflow: hidden;
  height: 100%;
  width: 100%;

  > div {
    position: absolute;
    height: 100%;
  }
}

.foreground {
  > div {
    width: calc(1696px * 4);

    background: {
      image: url("https://brendon.github.io/safari_flicker/foreground.png");
      position: left bottom;
      repeat: repeat-x;
      size: auto 74px;
    }
  }
}

It breaks down on very wide screens, but really, if you're rocking a 6000px wide window, good luck to you sir.

The way GSAP animates is that it changes the translateX value via javascript during a requestAnimationFrame (I think) so it's nice and smooth, and the flicker problem doesn't exist in this context.

Related