1

I have an animated PNG of about 30 frames, that animates on click request jquery. It is now displaying as a background inside a div that is hidden and being shown on click. However, as soon as it has been established it will not replay if I hide the div and show it again. If I refresh the page, it still does not work, it only works with a "hard refresh" of the page and it plays again on click, ONCE. I can not use a looped animated PNG since it needs to play from start to end on click and not loop.

Is there a way with jQuery to somehow make an animated PNG that is set to only play 1 time, to make it play again on request? Any solution is welcome as long as they work. There is nothing wrong with the code, the code makes the function play again, I have sound that replays. It is the animated PNG file that does not replay because it is a PNG set to only play ONCE. This is not a question about whether my code is correct, as I am only putting up the code as a quick example of how I have tried it out. But rather a question about how to make an animated PNG that is set to only play once, to make it play again, once, start till end, on any type of request, in this case on click.

HTML

<div id="animated-png"></div>

CSS

#animated-png {display:none;width:100px;height:100px;content:'';}
#animated-png-on {background:URL("URL/to/animated.png");display:block}

jQuery

$("#animated-png").on("click", function (e) {
e.stopPropagation();
$(this).addClass("animated-png-on");
$(this).delay(2000).queue(function () {
    $(this).removeClass("animated-png-on");
    $(this).dequeue();
});

Unfortunately, I can not upload an image example since the files are bigger than 2MB. It is a PNG sequence made into an animated PNG through ezgif website and set to play 1 time.

UPDATE:

Snor gave the solution on how to solve the APNG to play again and it works. Here are some results after looking into the WebM and WebP:

APNG: Resize it to the half size of your original and do this solution. 2MB WebM: No downsize, yet much lower quality with transparency. 3MB WebP: Doing it full-size was laggy compared to the APNG solution in full-size.

So the best solution for big animated transparent files would be the APNG and with Snor's solution. To make it lower in MB just do 50% or 60% of your original size and it will still look better than your WebM. Thank you.

Fredrik
  • 25
  • 5
  • Does this answer your question? [Animate.CSS Replay?](https://stackoverflow.com/questions/12399145/animate-css-replay) – Heretic Monkey Oct 13 '22 at 12:58
  • I did not find an answer in that thread, unfortunately, it is not that my code does not replay the function, that works perfectly. It is the PNG file that does not replay. – Fredrik Oct 13 '22 at 13:12

1 Answers1

1

Testing this, it appears that the browser implements a single instance of any APNG image regardless of how it is used in that session. That is, even if you append an <img src="animation.png" /> to the page and then later append a new similar element <img src="animation.png" /> or even an element <div style="background-image: url('animation.png');"></div> they will all always be in the exact same state of their animation. The animation begins when any of the elements first appear, and then it applies to them all.

Because JS has no control over APNG animation state, there is seemingly no way to reset this behaviour per image uri.

A very dirty solution:

You could set a randomly generated query string on the css for the element each time you want it to play again, eg:

.css('background','url("animation.png?12345")')

This will make the browser treat it as a new image, and load/animate it again. This of course means another http request to fetch the image (which you may want to pre-load so that it's ready to animate right away).

A better solution:

Is probably to not use an APNG at all for this due to this implementation limitation. Instead, if you put the 30 frames of the animation side by side in a single static .png image, then you can use either JS or a CSS animation to move the background position through the offsets for each frame of the animation, controlling it yourself.

Dirty solution example:

var apng = "https://upload.wikimedia.org/wikipedia/commons/4/40/Alarm_Clock_Animation_High_Res.png";
$(function(){
    $(".button").on("click", function (e) {
        e.preventDefault();
        $(".element").css("background-image",'url("' + apng + '?' + Math.random() + '")');
    });
});
.element {
    height: 720px;
    width: 908px;
    background-size: 100% 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class="button">reset animation</button>
<div class="element"></div>

Example with poster's non-looping image:

$(function(){
    $(".element").on("click", function (e) {
        e.preventDefault();
        var apng = "https://diam.se/apng/laser.png";
        $(this).addClass("attack-render")
            .css("background-image", 'url("' + apng + '?' + Math.random() + '")')
            .delay(3000)
            .queue(function () {
                $(this).removeClass("attack-render");
                $(this).dequeue();
        });
    });
});
.element {
    height: 510px;
    width: 720px;
    border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="element"></div>
Snor
  • 159
  • 1
  • 5
  • As I suspected, thank you for the confirmation. Got any good free JS sprite script in mind? The animation is about 1200x720px, making that into 30 frames becomes quite the large image to use as a sprite, I have not tested it so I can't say it will lag, but that's my first guess. If your "dirty" solution somehow actually works, then you solved the issue, the https request does not matter, I might have to make them download the product and use it locally anyway, so the pre-load will not matter I think. – Fredrik Oct 14 '22 at 13:21
  • @Fredrik I did test the "dirty" solution, and the animation will indeed start again each time you request it with a unique URL, so you just need to generate a random string and insert it in there each time. – Snor Oct 14 '22 at 13:32
  • @Fredrik Regarding a 1200x720px x 30 image, that does seem quite a bit! I have no idea how such things are optimised, so that could work absolutely fine -- or not! I don't have any JS script for this to hand, sorry, but I could write it and add it to the answer if you did want to do it that way even for such a large image! (Though there may be one readily available somewhere!) – Snor Oct 14 '22 at 13:36
  • How would you go about doing the randomly generated query string on the css for the element? I tried: var rand = Math.random(); $(".animated-png-on").css('background', 'url("../img/effects/laser.png?"' + rand + '")'); It did not play a second time. – Fredrik Oct 14 '22 at 13:43
  • @Fredrik I should add too in case it's of any help to you in this situation. If I was doing this with such a large animation I would probably look at turning it into a WebM file instead and playing it in a – Snor Oct 14 '22 at 13:44
  • it needs to be transparent, and better transparent than the gif file can offer, that's why I was using the APNG. Does it work with transparent WebM? – Fredrik Oct 14 '22 at 13:52
  • Ah, I see. I'm not familiar enough with WebM to answer that, but I suspect it's not. Anyway, I have updated my answer with a brief example of the dirty solution using a huge clock animation from Wikimedia. It should reset playing each time you click the button! – Snor Oct 14 '22 at 13:56
  • @Fredrik Having a quick look, it seems WebM can support transparency. It might be worth seeing if any apng > webm converter or any other service can generate one for you - it may not be something commonly supported. – Snor Oct 14 '22 at 14:01
  • It did not work, I see you are using a looped PNG, so the issue I have is that I can not use a looped png, the png i use is set to only play once. – Fredrik Oct 14 '22 at 14:05
  • Yeah I think I found something about the WebM, if you found anything worth looking at let me know, and would help others to find the answer to this also. Thank you for all your support. – Fredrik Oct 14 '22 at 14:06
  • @Fredrik Do you mean it did not reset at all when clicking the reset button? I've tested this in Chrome + Firefox now and it works fine for me in both. When I did this locally I did try it with a non-looping PNG, too (but couldn't find a publicly hosted one just now!) Both types of PNG work! And both reset with this method for me. I'm not sure what you are seeing, exactly? – Snor Oct 14 '22 at 14:10
  • this is how i used it inside a click function ---- var apng = "../img/effects/laser.png"; $(".attack-render").css("background-image", 'url("' + apng + '?' + Math.random() + '")'); $(this).addClass("attack-render").delay(3000).queue(function () { $(this).removeClass("attack-render"); $(this).dequeue(); }); ---- and it only played the first time like before – Fredrik Oct 14 '22 at 14:14
  • https://diam.se/apng/laser.png – Fredrik Oct 14 '22 at 14:17
  • @Fredrik You are trying to set the background-image on an element matching ".attack-render" BEFORE you have added the "attack-render" class to the element. So you are actually not setting the css on that element :) – Snor Oct 14 '22 at 14:21
  • Hah, true! So I tried this, did not play at all.. var apng = "../img/effects/laser.png"; $(this).addClass("attack-render").css("background-image", 'url("' + apng + '?' + Math.random() + '")').delay(3000).queue(function () { $(this).removeClass("attack-render"); $(this).dequeue(); }); – Fredrik Oct 14 '22 at 14:25
  • I have tried your code here with your image you provided now, and it re-plays every time I click on the element! Hmm! – Snor Oct 14 '22 at 14:28
  • style='background-image: url("../img/effects/laser.png?0.4725526148911735");' it only adds that once, it does not generate a new random number every click – Fredrik Oct 14 '22 at 14:29
  • I've updated the answer again using your code + your image. It re-plays the animation every time I click in the box. Is it not doing that for you? If not, the issue must be something outside of the code, such as your browser, etc? – Snor Oct 14 '22 at 14:33
  • No, your stuff works for me too here, awesome. The only difference I do is this: $(".player-first-attack").on("click", function (e) { e.stopPropagation(); --- without the function outside the click function. Is that needed? Plus I add a class to the .element first, showing the div, since it is hidden first. – Fredrik Oct 14 '22 at 14:35
  • Hmm, it seems to work the same for me with that. By the way, it's worth noting that with this example now, adding and removing the class actually does nothing with regards to the background setting, as the new background-image css is just being added to the matched element itself each time. But maybe you need that class adding/removing for some other reason. Anyway, everything seems to be working with this now from what I can see. If the code you have is still not working maybe you could post up a non-working example somewhere, as it's hard to find a problem in code that works! :) – Snor Oct 14 '22 at 14:40
  • Works. I had to write another url in the var than I use in the CSS and it worked. My bad. – Fredrik Oct 14 '22 at 14:42
  • thank you for your help and support, much appreciated. You helped a bunch, and I will look in to the WebM and see which performs best. – Fredrik Oct 14 '22 at 14:44
  • @Fredrik Ah, that's great to hear! I noticed with these large animated PNGs they really screw with the browser performance even on a high end machine. I don't think they are an optimised format for playing large high fps animations like this. So I am 99% certain a WebM would perform better, at least from that angle! – Snor Oct 14 '22 at 14:48
  • I did a test with both WebM and WebP, if you would like to hear the result I made an update. The best solution was to make APNG half-size from the original, stretch it out as background cover, contain or 200%, and use your function. Thank you. – Fredrik Oct 14 '22 at 15:31