Restart animated GIF as background-image

2019-01-15 06:37发布

Is it possible to restart an animated GIF used as background-image?

Consider this HTML:

<div id="face">
    <div id="eyes"></eyes>
</div>

And this style:

#eyes.blink {
    background-image:url('blink.gif');
}

I would like the blink.gif animation to play every time I add the class blink to #eyes, not just the first time.

I expected this to work:

function startBlink() {
    $('#eyes').addClass('blink');
}

function stopBlink() {
    $('#eyes').removeClass('blink');
}

The problem is that both Firefox and WebKit browser do not play a background-image GIF animation again once it has played once. Adding/removing the class blink only works the first time.

8条回答
ゆ 、 Hurt°
2楼-- · 2019-01-15 07:00

There is an alternative that does not reload the GIF every time and waste bandwidth.

It involves storing the GIF as Base64 in memory (circumventing browser cache), and uses the FileReader API (which seems to be supported in all modern browsers). Note that loading images this way is subject to cross-origin policy (unlike the image reload solutions.)

Update: Browser caching is getting smarter about caching background image data URI's, causing the animation not to start over. I found I had to add a cache-busting random string to the data url now (which according to the DataURI Scheme, should be considered an optional attribute. Tested in Chrome & IE Edge.)

See it in action: http://jsfiddle.net/jcward/nknLrtzL/10/

Here's how it works. This function loads the image as a Base64-encoded string.

function toDataUrl(url, callback) {
  var xhr = new XMLHttpRequest();
  xhr.onload = function() {
    var reader = new FileReader();
    reader.onloadend = function() {
      callback(reader.result);
    }
    reader.readAsDataURL(xhr.response);
  };
  xhr.open('GET', url);
  xhr.responseType = 'blob'; // IE11, set responseType must come after .open()
  xhr.send();
}

Then, any time you want to restart the GIF animation, change the background-image property to none, then the base64 string (in some browsers, you need to re-add the child to trigger the update without a setTimeout):

$div.css({backgroundImage: "none"});
$div.parent().add($div); // Some browsers need this to restart the anim
// Slip in a cache busting random number to the data URI attributes
$div.css({backgroundImage: "url("+img_base64.replace("image/gif","image/gif;rnd="+Math.random())+")"});

Thanks to this answer for the toDataURL function (with fix for IE11.)

查看更多
甜甜的少女心
3楼-- · 2019-01-15 07:03

I combined several parts of the solution to make one whole solution that solves (hopefully) all problems:

  • Determine the background-image URL of an element (from css background-image)
  • Trigger a restart for that image WITHOUT reloading it from the web
  • Restarting it in all places (without touching each individually)
  • Making sure the target is repainted without artifacts after restarting the animation

In my solution i create helper images that are added to the body but hidden in a way so they are still rendered by the browser but won't interact with the page visually using position: absolute; left: -5000px;.

A reference to our helper images is cached in resetHelperImages so we can reuse them for the same image in subsequent calls.

I am using jQuery for my example, but it could be adapted to work without jQuery, too.

Tested in: Chrome (Version 43.0.2357.130 m)

var resetHelperImages = {};

function restartAnimation(elem) {
  elem = $(elem);
  for (var i = 0; i < elem.length; i++) {
    var element = elem[i];
    // code part from: http://stackoverflow.com/a/14013171/1520422
    var style = element.currentStyle || window.getComputedStyle(element, false);
    // var bgImg = style.backgroundImage.slice(4, -1).replace(/"/g, '');
    var bgImg = style.backgroundImage.match(/url\(([^\)]+)\)/)[1].replace(/"/g, '');
    // edit: Suggestion from user71738 to handle background-images with additional settings

    var helper = resetHelperImages[bgImg]; // we cache our image instances
    if (!helper) {
      helper = $('<img>')
        .attr('src', bgImg)
        .css({
          position: 'absolute',
          left: '-5000px'
        }) // make it invisible, but still force the browser to render / load it
        .appendTo('body')[0];
      resetHelperImages[bgImg] = helper;
      setTimeout(function() {
        helper.src = bgImg;
      }, 10);
      // the first call does not seem to work immediately (like the rest, when called later)
      // i tried different delays: 0 & 1 don't work. With 10 or 100 it was ok.
      // But maybe it depends on the image download time.
    } else {
      // code part from: http://stackoverflow.com/a/21012986/1520422
      helper.src = bgImg;
    }
  }
  // force repaint - otherwise it has weird artefacts (in chrome at least)
  // code part from: http://stackoverflow.com/a/29946331/1520422
  elem.css("opacity", .99);
  setTimeout(function() {
    elem.css("opacity", 1);
  }, 20);
}
.myBgImageClass {
  background-image: url('http://i410.photobucket.com/albums/pp184/OllieMarchant/Countup.gif');
  width: 100px;
  height: 150px;
  background-size: 100%;
  background-repeat: no-repeat;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="myBgImageClass"></div>
<button onclick="restartAnimation($('.myBgImageClass'))">restart</button>

查看更多
我命由我不由天
4楼-- · 2019-01-15 07:04

For some reason this works:

// Append the image to the page
var i = new Image();
i.src = 'some.gif';
document.body.appendChild(i);

// Now execute this line and the gif will restart
// (anywhere it appears on the page, including CSS backgrounds)
i.src = 'some.gif';

This requires an actual image DOM element to be appended to the page, but you can hide it with visibility: hidden. This doesn't require the image to be downloaded over the network multiple times.

I only tested this in Firefox and Chrome. Not sure about other browsers.

查看更多
叼着烟拽天下
5楼-- · 2019-01-15 07:05

Just because I still need this every now and then I figured the pure JS function I use might be helpful for someone else. This is a pure JS way of restarting an animated gif, without reloading it. You can call this from a link and/or document load event.

<img id="img3" src="../_Images/animated.gif">

<a onClick="resetGif('img3')">reset gif3</a>

<script type="text/javascript">

// reset an animated gif to start at first image without reloading it from server.
// Note: if you have the same image on the page more than ones, they all reset.
function resetGif(id) {
    var img = document.getElementById(id);
    var imageUrl = img.src;
    img.src = "";
    img.src = imageUrl;
};

</script>

On some browsers you only need to reset the img.src to itself and it works fine. On IE you need to clear it before resetting it. This resetGif() picks the image name from the image id. This is handy in case you ever change the actual image link for a given id because you do not have to remember to change the resetGiF() calls.

--Nico

查看更多
我命由我不由天
6楼-- · 2019-01-15 07:07

Regarding this answer posted by Frederic Leitenberger, I found it to work wonderfully.

However, it breaks down if your background-image has multiple, layered parts, like this:

background-image: url(https://upload.wikimedia.org/wikipedia/commons/5/53/Google_%22G%22_Logo.svg),
    radial-gradient(ellipse at center,
        rgba(255,255,255,1) 0%,
        rgba(255,255,255,1) 50%,
        rgba(255,255,255,0) 80%);

To get around this limitation, I modified the line that finds the background image url, like so:

var bgImg = style.backgroundImage.match(/url\(([^\)]+)\)/)[1].replace(/"/g, '');

This uses a regular expression to extract just the URL portion of the background-image.

I would have added this as a comment to the linked answer, but I'm a noob without reputation, so was blocked from doing so. Those with adequate rep may want to add the line to the actual answer.

查看更多
叛逆
7楼-- · 2019-01-15 07:14

I've found you can also add a ?+Math.random() to the end of the picture src and it'll reload the .gif.

查看更多
登录 后发表回答