Logan
Logan

Reputation: 1147

Can you control GIF animation with Javascript?

Is it possible to use javascript to control which frame of a GIF image is showing and/or stop the animation. I want to be able to load a GIF with several frames and only show one of them.

I know I could do it with lots of separate images, but if I can do what I want with a GIF, it will only be one file to worry about.

Upvotes: 88

Views: 147267

Answers (5)

Yogi
Yogi

Reputation: 7334

Update 2024

This question was posted in 2010 and is in need of an update. The previous answers use techniques and libraries that are rarely used anymore. Yet, it continues to be cited as a solution.

screenshot enter image description here

Browsers now support the video element. These will not play animated GIF images directly. Yet, it's very easy to convert GIF images to MP4 format using graphics applications or one of the many online converters. The WEBM format is a widely supported alternative when a transparent background is required.

Video has many advantages over an animated GIF. A few of these are:

  • Greatly reduced file size
  • No libraries are required
  • Plays while loading
  • Ability to use media sources
  • Visually the same as the GIF
  • And more...

Code Snippet

The snippet plays an MP4, WEBM, and GIF side by side. The MP4 and WEBM use an HTML-5 video element. The GIF uses the library from the most upvoted answer. There are buttons for play, pause, and seek.

const toggle = document.querySelector('input[type=checkbox]');
const mp4 = document.querySelector('#mp4 video');
const webm = document.querySelector('#webm video');
const gif = new SuperGif({
  gif: document.querySelector('#gif img'),
  loop_mode: true,
  auto_play: true,
  max_width: 150,
  // on_end:.
  // loop_delay:
  show_progress_bar: false
});

gif.load();

play.addEventListener('click', () => {
  mp4.play();
  webm.play();
  gif.play();
})

pause.addEventListener('click', () => {
  mp4.pause();
  webm.pause();
  gif.pause();
})

seek.addEventListener('change', () => {
  let percent = seek.value / 100;
  let frame = Math.floor(gif.get_length() * percent)
  gif.move_to(frame);
  let position = Math.floor(mp4.duration * percent);
  mp4.currentTime = position;
  position = Math.floor(webm.duration * percent);
  webm.currentTime = position;
})

mp4.addEventListener('timeupdate', () => {
  seek.value = Math.floor(100 * mp4.currentTime / mp4.duration);
});
body {
  font-family: sans-serif;

.container {
  margin-top: 1rem;
  display: flex;
  background-image: linear-gradient(to bottom right, red, yellow);
}

input[type=button] {
  min-width: 4rem;
}
input[type=range] {
  min-width: 20rem;
}
figure {
  border: thin solid white;
  border-radius: 0.5rem;
  display: inline-block;
  padding: 0.25rem;
  margin: 0.5rem;
  background-color: transparent;
}

figcaption {
  color: white;
  text-align: center;
}

video,
img {
  width: 150px;
  height: auto;
}
<input type="button" id="play" value="Play">
<input type="button" id="pause" value="Pause">
<input type="range" id="seek" min="0" max="100" value="0">

<div class="container">
  <figure id="mp4">
    <figcaption>
      205kb Video MP4
    </figcaption>
    <video autoplay muted loop>
      <source src="https://i.yourimageshare.com/i0rmDubApf.mp4">
    </video>
  </figure>

  <figure id="webm">
    <figcaption>
      222kb Video WEBM
    </figcaption>
    <video autoplay muted loop>
      <source src="https://i.yourimageshare.com/S1S9fJww3M.webm">
    </video>
  </figure>

  <figure id="gif">
    <figcaption>
      746kb Animated GIF
    </figcaption>
    <image src="https://i.postimg.cc/t4zZRyZH/rubik.gif" />
  </figure>
</div>



<script src="https://cdn.jsdelivr.net/gh/buzzfeed/libgif-js/libgif.js"></script>

Upvotes: 10

user
user

Reputation: 25838

If you are OK with converting your gif to a sprite sheet, you can do it this way (using ImageMagick):

montage animation.gif -coalesce -tile x1 -geometry +0+0 -background None -quality 100 spritesheet.png

It is even likely that the new image will be of lesser size.

Once you have a sprite sheet, use CSS animation. An animation with a fixed frame time is used here:

var el = document.getElementById('anim');
function play() {
  el.style.animationPlayState = 'running';
}
function pause() {
  el.style.animationPlayState = 'paused';
}
function reset() {
  el.style.animation = 'none';
  el.offsetHeight; /* trigger reflow to apply the change immediately */
  el.style.animation = null;
}
function stop() {
  reset();
  pause();
}
#anim {
  background-image: url('https://i.sstatic.net/J5drY.png');
  width: 250px;
  height: 188px;
  animation: anim 1.0s steps(10) infinite;
}
@keyframes anim {
  100% { background-position: -2500px; }
}
<div id="anim" title="Animated Bat by Calciumtrice"></div>
<button onclick="play()">Play</button>
<button onclick="pause()">Pause</button>
<button onclick="reset()">Reset</button>
<button onclick="stop()">Stop</button>

If you want the animation to not start automatically, add paused to the end of animation rule.

Upvotes: 38

Charlotte
Charlotte

Reputation: 1263

You can use the libgif library.

It allows you to start/stop the gif and control which frame the gif is on.

<script type="text/javascript" src="./libgif.js"></script>
<img src="./example1_preview.gif" rel:animated_src="./example1.gif"
 width="360" height="360" rel:auto_play="1" rel:rubbable="1" />

<script type="text/javascript">
    $$('img').each(function (img_tag) {
        if (/.*\.gif/.test(img_tag.src)) {
            var rub = new SuperGif({ gif: img_tag } );
            rub.load(function(){
                console.log('oh hey, now the gif is loaded');
            });
        }
    });
</script>

(most of the code is taken directly from their example)

Upvotes: 71

Onevarez
Onevarez

Reputation: 1154

I use x-gif it's pretty cool and easy to setup.

From Github:

<x-gif src="probably_cats.gif"></x-gif>

Where you can add the following as attributes:

  • Playback modes:
    • speed="1.0" (default mode) multiplies the speed by the value of the attribute;
    • sync defers playback to an external object;
    • bpm="120" syncs GIFs to a given beats-per-minute;
  • Options:

    • stopped prevents the GIF from animating;

    • fill causes the GIF to expand to cover its container;

    • n-times="3.0" (speed mode only) stops playback (by adding the attribute stopped) after a set number of times;
    • snap (sync & bpm modes only) instead of allowing longer GIFs to sync to multiple beats, force them to fit into only one;
    • ping-pong plays the GIF front-to-back then back-to-front;
  • Debugging:
    • debug turns on debug output from the Gif Exploder;
    • exploded stops playback, and renders each frame out side-by-side.

Upvotes: 35

N 1.1
N 1.1

Reputation: 12544

You can do it with a single image using CSS sprites.

Upvotes: 13

Related Questions