Restart animated GIF as background-image

31,399

Solution 1

You can get the animated gif to replay by reloading it. This isn't ideal for bandwidth, especially if your image is large, but it will force a restart of the animation.

In my example I'm adding and removing it onclick of <div id="animated">:

$('#animated').click(function() {

    /* Reference to the clicked element and toggle the .go class */
    var $div = $(this);
    $div.toggleClass('go');

    /* Start the animated gif */
    if ($div.hasClass('go')) {

        /* Create an <img> element and give it the animated gif as a src.  To 
           force a reload we add a date parameter to the URL */
        var img = document.createElement('img');
        img.src = "http://yoursite.com/animated.gif?p" + new Date().getTime();

        /* Once the image has loaded, set it as the background-image */
        $(img).load(function(){
            $div.css({backgroundImage: "url("+img.src+")"});
        });

    /* Remove the background-image */        
    } else {
       $div.css({backgroundImage: "none"});
    }
})

Demo of it in action.

Solution 2

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

Solution 3

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.)

Solution 4

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>

Solution 5

Have you considered using the same image twice called blink.gif and blink2.gif, adding two classes for them and toggling between classes?

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

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

.blink2 {
    background-image:url('blink2.gif');
}

function MakeBlink()
{
   if ($('#eyes').hasClass('blink'))
   {
      $('#eyes').removeClass('blink').addClass('blink2');
   } else
   {
     $('#eyes').removeClass('blink2').addClass('blink');
   }
}
Share:
31,399
hpique
Author by

hpique

iOS, Android &amp; Mac developer. Founder of Robot Media. @hpique

Updated on July 09, 2022

Comments

  • hpique
    hpique almost 2 years

    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.

  • Lekensteyn
    Lekensteyn over 12 years
    This would be the way to go for a two-frames animation ("blink"). Gif animations can be cancelled by hitting the Esc button on the keyboard.
  • Ivan
    Ivan over 12 years
    Is there really a need to have a different image? I can't see the reason why $('#eyes').removeClass('blink').addClass('blink'); wouldn't work
  • Quentin
    Quentin over 12 years
    @Ivan — Presumably the OP wants the eyes to blink by having the eyelids shut, and not by disappearing.
  • Ivan
    Ivan over 12 years
    Well in this case it's even better to use the same image. If blink2.gif is not preloaded then background will disappear for considerable time. If we use the same image then it's just a matter of removing and adding same class which should take same amount of time as removing and applying a different class sans loading a new image.
  • krupali
    krupali over 12 years
    I would advise preloading blink2.gif but they should be different names to make sure the browser does not assume there has been no style change and therefore skip the animation
  • hpique
    hpique over 12 years
    @Richard Unfortunately this doesn't work. From the third time MakeBlink is executed the GIF animations do not play anymore (basically, it exhibits the same behavior than with 1 GIF).
  • hpique
    hpique over 12 years
    @Ivan 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 will only work once.
  • c69
    c69 over 12 years
    why you add random number to file path ? to lag ? - simply resetting src is enough for animation restart. jsfiddle.net/Lqx2V
  • Pat
    Pat over 12 years
    @c69 it's to force it to not use the browser cache. Without this Webkit wasn't restarting the animation.
  • hpique
    hpique over 12 years
    It should be noted that $eyes.css('background-image', 'url(' + 'blink.gif?d=' + new Date().getTime() + ')'); is enough. No need to use an auxiliary img.
  • Abram
    Abram over 9 years
    Even better, you can add '#' + Math.random() to the URL, and then presumably the browser doesn't have to reload it. I have tested this in Chrome, and it works (it is retrieved from cache).
  • Abram
    Abram over 9 years
    I simply added '#' + Math.random() to the URL, and it restarted the animation while using cache. Tested with Chrome 38.0.2125.111
  • cat
    cat about 9 years
    '#' + Math.random() fails with Chrome 42.0.2311.135, but '?' + Math.random() works :-(
  • Hayko Koryun
    Hayko Koryun about 9 years
    @Abram, adding # to the URL instead of ? doesn't seem to work anymore FWIW
  • Frederic Leitenberger
    Frederic Leitenberger almost 7 years
    I added your improvement to my solution. Thanks!
  • Frederic Leitenberger
    Frederic Leitenberger almost 7 years
    @jnkb yes, you are right, it does not work in IE. Maybe a general solution for all browsers would be to download and cache the raw data of the image and then create a data:-url from it to restart the animation. Maybe data:-url with cache invalidation ... is that possible?
  • supersan
    supersan over 2 years
    This is the best answer!