linasmnew
linasmnew

Reputation: 3977

CSS Animation Not Working As Expected With Image element In Safari

Below is a minimum mockup of a bug that I'm experiencing in Safari.


Expected behaviour: The code is supposed to display a placeholder while another image loads and then fade-in that image while removing the placeholder.

placeholder -> fading in loaded image

Actual behaviour: Works as expected in Chrome and Mozilla but fails in Safari, resulting in the following effect:

placeholder -> white screen -> loaded image

Can somebody help me figure out why this is happening in Safari please? (try running below example in chrome or mozilla vs safari to see for your self.)

const image = new Image();

image.onload = () => {
  document.getElementById('placeholder').remove();

  const el = document.createElement('img');
  el.setAttribute('src', 'http://deelay.me/1000/https://cdn.pixabay.com/photo/2014/06/03/19/38/board-361516_1280.jpg');
  el.setAttribute('width', '150');
  el.setAttribute('height', '150');

  el.style = `
    animation-name: testAnimation;
    animation-duration: 0.3s;
    animation-iteration-count: 1;
    animation-timing-function: ease-in;
    background: red;
  `;

  document.body.appendChild(el);
}

image.src = 'http://deelay.me/1000/https://cdn.pixabay.com/photo/2014/06/03/19/38/board-361516_1280.jpg';
#placeholder {
  background: #eee;
}

@-webkit-keyframes testAnimation {
  0% {
    opacity: 0.25;
  }
  50% {
    opacity: 0.5;
  }
  100% {
    opacity: 1;
  }
}
<img id="placeholder" src="data:image/svg+xml;charset=utf-8,%3Csvg xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' width%3D'200' height%3D'300' viewBox%3D'0 0 200 300'%2F%3E" width="150" height="150">

(

Another weird behaviour is that if animation-duration gets increased to around 3-4 seconds then the image does fade-in but the white screen is still there, i.e.

placeholder -> white screen -> fading in loaded image

)

update:

After playing around with img’s background color it seems like in Safari the animation begins taking effect around 1s before the image even begins getting rendered to the screen.

Whereas in Chrome and Mozilla the animation appears to be perfectly in sync with the image.

Run updated code to see for yourself

(it also only seems to happen with <img>, I've tested it with some other elements (still inside of image.onload) and they work as expected.)

update 2: See the fix in the answer below together with an explanation

Upvotes: 4

Views: 1975

Answers (2)

linasmnew
linasmnew

Reputation: 3977

This was fixed by adding another onload event this time on the image element rather than on the explicit Image object and putting the image.onload listener inside of it.

Which seems to suggest that in Safari, animation and styles are applied as soon as the <img> is added to the DOM - before it is fully loaded (its onload event called.)

  • This can be tested by giving the <img> a background color and seeing it show up together with the animation as soon as the element is added to the DOM and seeing that it takes an extra second until the image itself becomes visible

  • You can also see the same effect happen without using any animation at all just by giving the <img> a background color

(a bug in Safari? My Safari version: Version 11.0.2 (13604.4.7.1.3))

Edit: Actually the problem seems to be caused by caching, Safari doesn't seem to cache the pre-loaded proxied image while other browsers do cache it. Safari seems to fire another request for that image when it gets rendered to the screen - hence the styles being visible prior to seeing the actual image since the image is still being downloaded.

Edit 2: Upon further investigation it seems like Safari is actually the only one out of those 3 browsers that is behaving as expected - this is because the image's HTTP response contains a Cache-Control: no-cache, must-revalidate header explicitly instructing the browser to not cache the image.

const image = new Image();
    
const el = document.createElement('img');
el.setAttribute('src', 'http://deelay.me/500/https://cdn.pixabay.com/photo/2014/06/03/19/38/board-361516_1280.jpg');
el.setAttribute('width', '150');
el.setAttribute('height', '150');
el.style = `
  animation-name: testAnimation;
  animation-duration: 1s;
  animation-iteration-count: 1;
  animation-timing-function: ease-in;
  background-color: red;
`;
    
el.onload = () => {
  image.onload = () => {
    document.getElementById('placeholder').remove();
    document.body.appendChild(el);
  }
}

image.src = 'http://deelay.me/500/https://cdn.pixabay.com/photo/2014/06/03/19/38/board-361516_1280.jpg';
#placeholder {
  background: #eee;
}

@-webkit-keyframes testAnimation {
  0% {
    opacity: 0.25;
  }
  50% {
    opacity: 0.5;
  }
  100% {
    opacity: 1;
  }
}
<img id="placeholder" src="data:image/svg+xml;charset=utf-8,%3Csvg xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg' width%3D'200' height%3D'300' viewBox%3D'0 0 200 300'%2F%3E" width="150" height="150">

Upvotes: 3

Criminally Inane
Criminally Inane

Reputation: 216

This appears to be related to the specific server/image you are using. I tried changing just the image URL (to something on wikimedia.org, just for testing purposes), and the animation began working as expected in Safari 11.0.2 (13604.4.7.1.6).

Pre-loading the image may help... or even better (all of this assuming you have proper usage rights, of course), you can try copying the image to your web server so it can be loaded locally. Essentially, you're just remedying the fact that Safari has finished running the animation by the time it can load the image from the remote server. You could also try using window.onload instead of image.onload, to force all external resources to be loaded before it runs.

Upvotes: 1

Related Questions