Reputation:
const button = document.getElementById('healer')
const preloaded = new Image()
preloaded.src = "https://svgshare.com/i/KzN.svg"
const img = document.getElementById("heal")
//img.style.display = "none"
button.onclick = () => {
button.disabled = true
img.src = "https://svgshare.com/i/KzN.svg"
img.style.display = ""
setTimeout(() => {
button.disabled = false
img.src = ""
//img.style.display = "none"
}, 1000)
}
#monster {
height: 160px;
width: 160px;
}
#heal {
height: 160px;
width: 160px;
position: absolute;
top: 0;
left: 0;
}
<img id="monster" src="https://i.imgur.com/NMocKWy.png">
<img id="heal">
<button id="healer">
Show heal anim
</button>
As you can see, only the first click produces the animation. Subsequent clicks have no effect. I think this is because the SVG is set to only run animation once (SVG source code), and the browser does not reset the animation even when the img
tag's src
attribute is set to nothing and then to the SVG again (even though according to this Stackoverflow thread resetting src
should reset the animation: Proper way to reset a GIF animation with display:none on Chrome).
Setting the SVG to run the animation infinitely (modified SVG source code) is only slightly better:
const button = document.getElementById('healer')
const preloaded = new Image()
preloaded.src = "https://svgshare.com/i/L0q.svg"
const img = document.getElementById("heal")
//img.style.display = "none"
button.onclick = () => {
button.disabled = true
img.src = "https://svgshare.com/i/L0q.svg"
img.style.display = ""
setTimeout(() => {
button.disabled = false
img.src = ""
//img.style.display = "none"
}, 1000)
}
#monster {
height: 160px;
width: 160px;
}
#heal {
height: 160px;
width: 160px;
position: absolute;
top: 0;
left: 0;
}
<img id="monster" src="https://i.imgur.com/NMocKWy.png">
<img id="heal">
<button id="healer">
Show heal anim
</button>
It seems the browser keeps running the anim internally so again only the first button click is correct, subsequent clicks start the anim from the middle and end it in the middle as well, which I believe looks suboptimal.
Is there any way to make this work as intended? (I'm trying to implement healing animation for a Pokemon-like browser based game)
I suppose I could make it work if I inlined the SVG in HTML code since this would give me direct access to styling, and resetting the style AFAIK DOES restart the animation, but I find inlining graphics in HTML ugly. Is doing so my only option?
BTW display: none'
is commented out since as per the linked SO question it might mess with animation resetting (doesn't change anything for me though). Preloading doesn't seem to change much for me as well, but is needed due to svgshare's slow responsiveness. I'm on Firefox, Windows.
Upvotes: 1
Views: 1339
Reputation: 136708
That's a very weird behavior indeed, but you can trick it by appending a random #
fragment url.
This will force the browser to reparse the whole document, but not to fetch again the content, since it will be cached already.
Here is an example that will work only in Firefox:
const button = document.getElementById('healer')
const preloaded = new Image()
preloaded.src = "https://svgshare.com/i/KzN.svg"
const img = document.getElementById("heal")
img.style.display = "none"
button.onclick = () => {
button.disabled = true
img.src = "https://svgshare.com/i/KzN.svg#" + Math.random();
img.style.display = ""
setTimeout(() => {
button.disabled = false
img.src = ""
img.style.display = "none"
}, 1000)
}
#monster {
height: 160px;
width: 160px;
}
#heal {
height: 160px;
width: 160px;
position: absolute;
top: 0;
left: 0;
}
<img id="monster" src="https://i.imgur.com/NMocKWy.png">
<img id="heal">
<button id="healer">
Show heal anim
</button>
To make it work in Chrome, you have to change your svg so that the animation
is set on the <use>
elements instead of the <polygon>
ones:
// For Chrome we need to fix OP's svg image
// we will thus create a blob:// URI
const content = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="-80 -80 160 160">
<style>
polygon {
fill: lime;
stroke: green;
stroke-width: 4px;
}
use {
animation: linear both moveCross 0.4s var(--delay);
}
@keyframes moveCross {
from {
transform: translate(var(--x-off), 99px) scale(1);
}
to {
transform: translate(var(--x-off), 0) scale(0);
}
}
</style>
<defs>
<polygon id="cross" points="-5,-15, 5,-15, 5,-5, 15,-5, 15,5, 5,5, 5,15, -5,15, -5,5, -15,5, -15,-5, -5,-5"/>
</defs>
<!-- twice as many crosses for moves stronger than Wait, like Rejuvenate? -->
<use hef="#cross" style="--x-off: -61.0px; --delay: 0.00s;"/>
<use href="#cross" style="--x-off: 30.5px; --delay: 0.15s;"/>
<use href="#cross" style="--x-off: -30.5px; --delay: 0.30s;"/>
<use href="#cross" style="--x-off: 61.0px; --delay: 0.45s;"/>
<use href="#cross" style="--x-off: 0.0px; --delay: 0.60s;"/>
</svg>`;
const blob = new Blob([content], {
type: "image/svg+xml"
});
const url = URL.createObjectURL(blob);
const button = document.getElementById('healer')
const img = document.getElementById("heal")
img.style.display = "none"
button.onclick = () => {
button.disabled = true;
img.src = url + '#' + Math.random();
img.style.display = ""
setTimeout(() => {
button.disabled = false
img.src = "";
img.style.display = "none"
}, 1000)
}
#monster {
height: 160px;
width: 160px;
}
#heal {
height: 160px;
width: 160px;
position: absolute;
top: 0;
left: 0;
}
<img id="monster" src="https://i.imgur.com/NMocKWy.png">
<img id="heal">
<button id="healer">
Show heal anim
</button>
Upvotes: 1