Reputation: 5024
I can't animate a svg that I import using the img
tag. I can't hard code the svg, as my project generates it during pre-processing using webpack. Sadly, it doesn't seems like I could fetch my file through the svg
tag, as I'm not aware of any "src" or "href" attribute.
How can I animate a SVG who is not hard coded ?
Upvotes: 0
Views: 193
Reputation: 136766
That really depends on how is powered your animation.
There are basically three ways to generate an animated SVG:
How do these behave when their <svg>
documentElement is embedded in an <img>
tag?
<img>
.In supporting browsers, these would run just normally, as if your SVG was not embedded.
The only restrictions you would face are
element.click
and alike events won't workBoth limitations do not affect SVG loaded in <object>
, <embed>
or <iframe>
, so you could well use it instead if you need it.
var svgStr = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50" height="50">
<rect id="rect" x="-30" y="0" width="30" height="50">
<!-- this will work normally, even in an <img> -->
<animate attributeType="XML" attributeName="x" from="-30" to="50"
begin="0s" dur="10s" repeatCount="indefinite"/>
<!-- this will not work in <img>, but will in <object>/<iframe>/<embed> -->
<animate attributeType="XML" attributeName="fill" from="blue" to="red"
begin="rect.click"
dur="1s" repeatCount="1"/>
</rect>
<!-- js-based workaround won't work in <img> but will in <object>/<iframe>/<embed> -->
<script src="https://cdn.rawgit.com/FakeSmile/FakeSmile/23c5ceae/smil.user.js"><\/script>
</svg>`;
loadSVG(document.images[0]);
loadSVG(document.querySelector('object'));
function loadSVG(container) {
var url = URL.createObjectURL(new Blob([svgStr], {type: 'image/svg+xml'}));
container.src = container.data = url;
}
img{
border: 1px solid green;
}
object{
border: 1px solid blue;
}
<img src="">
<object></object>
<div>Try to click the black rectangle in both the <code><img></code> and <code><object></code> tags.
<img>
.Just like SMIL animations, they should work in supporting browsers, with the same user-gesture limitations, and the same possible ways around (use an other container):
var svgStr = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50" height="50">
<rect id="rect" x="0" y="0" width="30" height="50"/>
<defs>
<style>
#rect {
/* this will work normally, even in an img */
animation: move 10s linear infinite;
}
#rect:hover {
/* this will not work in img, but will in object/iframe/embed */
animation: move 10s linear infinite, color 1s 1;
}
@keyframes move {
from {
transform: translate(-30px, 0px);
}
to {
transform: translate(50px, 0px);
}
}
@keyframes color {
from {
fill: blue;
}
to {
fill: red;
}
}
</style>
</defs>
</svg>`;
loadSVG(document.images[0]);
loadSVG(document.querySelector('object'));
function loadSVG(container) {
var url = URL.createObjectURL(new Blob([svgStr], {type: 'image/svg+xml'}));
container.src = container.data = url;
}
img{
border: 1px solid green;
}
object{
border: 1px solid blue;
}
<img src="">
<object></object>
<div>Try to mouse hover the black rectangle in both the <code><img></code> and <code><object></code> tags.
<img>
.These will simply not work. SVG documents embedded in <img>
tag can not be scripted.
To workaround this, use either an <object>
, an <embed>
or an <iframe>
element as container.
var svgStr = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50" height="50">
<rect id="rect" x="0" y="0" width="30" height="50"/>
<script type="application/javascript">
// will simply never work in img
var x = 0, rect = document.getElementById('rect');
function anim() {
x = (x + 1) % 80;
rect.setAttribute('x', x - 30);
requestAnimationFrame(anim);
}
anim();
<\/script>
</svg>`;
loadSVG(document.images[0]);
loadSVG(document.querySelector('object'));
function loadSVG(container) {
var url = URL.createObjectURL(new Blob([svgStr], {type: 'image/svg+xml'}));
container.src = container.data = url;
}
img{
border: 1px solid green;
}
object{
border: 1px solid blue;
}
<img src="">
<object></object>
So basically, SVG in <img>
comes with a lot of restrictions, that can all be overwhelmed by using an other container.
Now, every container will come with its own restrictions:
<iframe>
will not resize to its content, it will also come by default with a border and some other ugly things.<object>
and <embed>
will get unloaded by webkit browsers when not visible (display: none
), and won't be cached in any browser...And of course, there is also the possibility to fetch your SVG markup through AJAX and to load it inside your actual HTML page, but I personally can't advice to do so:
And since we're here, an other limitation of SVG in <img>
is that it can not load any resources outside its own markup, everything needs to be included in it directly, even fonts and raster images.
Upvotes: 2