如何为非硬编码的 SVG 制作动画?
How can I animate a SVG who is not hard coded?
我无法为使用 img
标签导入的 svg 制作动画。我无法对 svg 进行硬编码,因为我的项目是在使用 webpack 进行预处理期间生成的。可悲的是,我似乎无法通过 svg
标签获取我的文件,因为我不知道任何 "src" 或 "href" 属性。
如何为非硬编码的 SVG 设置动画?
这实际上取决于 为 动画提供支持的方式。
基本上可以通过三种方式生成动画 SVG:
- SMIL Animation - 不幸的是没有得到广泛支持(但你确实用 [svg-animate] 标记了,所以让我们把它作为第一种情况)。
- CSS Animation - 随着SVG2的出现,我敢打赌这些会变得越来越普遍。
- 基于脚本的动画。
当它们的 <svg>
documentElement 嵌入到 <img>
标签中时,它们的行为如何?
<img>
中的 SMIL 动画。
在支持的浏览器中,这些会 运行 正常,就好像您的 SVG 没有嵌入一样。
您将面临的唯一限制是
- 您不会收到任何 user-gesture,因此
element.click
和类似的事件将不起作用
- 对于不支持 SMIL 动画 (IE...) 的浏览器,您无法回退到基于脚本的动画。
这两个限制都不影响在 <object>
、<embed>
或 <iframe>
中加载的 SVG,因此如果需要,您可以改用它。
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.
CSS 动画 <img>
.
就像 SMIL 动画一样,它们应该在支持浏览器的情况下工作,具有相同的 user-gesture 限制和相同的可能方式(使用其他容器):
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>
中基于脚本的动画。
这些根本行不通。无法编写嵌入在 <img>
标记中的 SVG 文档。
要解决此问题,请使用 <object>
、<embed>
或 <iframe>
元素作为容器。
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>
所以基本上,<img>
中的 SVG 有很多限制,使用其他容器都可以克服这些限制。
现在,每个容器都有自己的限制:
<iframe>
不会根据其内容调整大小,它还会默认带有边框和其他一些丑陋的东西。
<object>
和 <embed>
将在不可见时被 webkit 浏览器卸载 (display: none
),并且不会在任何浏览器中缓存...
当然,也可以通过 AJAX 获取您的 SVG 标记并将其加载到您的实际 HTML 页面中,但我个人不建议这样做:
- 您需要确保没有重复的 id 元素,
- 您需要确保所有 CSS 规则都足够具体,不会影响页面中的其他元素,
- 您必须确保只加载受信任的代码,因为脚本会 运行,也就是说您对 XSS 攻击敞开大门。
既然我们在这里,<img>
中 SVG 的另一个限制是它无法加载其自身标记之外的任何资源,所有内容都需要直接包含在其中,甚至是字体和光栅图像.
我无法为使用 img
标签导入的 svg 制作动画。我无法对 svg 进行硬编码,因为我的项目是在使用 webpack 进行预处理期间生成的。可悲的是,我似乎无法通过 svg
标签获取我的文件,因为我不知道任何 "src" 或 "href" 属性。
如何为非硬编码的 SVG 设置动画?
这实际上取决于 为 动画提供支持的方式。
基本上可以通过三种方式生成动画 SVG:
- SMIL Animation - 不幸的是没有得到广泛支持(但你确实用 [svg-animate] 标记了,所以让我们把它作为第一种情况)。
- CSS Animation - 随着SVG2的出现,我敢打赌这些会变得越来越普遍。
- 基于脚本的动画。
当它们的 <svg>
documentElement 嵌入到 <img>
标签中时,它们的行为如何?
<img>
中的 SMIL 动画。
在支持的浏览器中,这些会 运行 正常,就好像您的 SVG 没有嵌入一样。
您将面临的唯一限制是
- 您不会收到任何 user-gesture,因此
element.click
和类似的事件将不起作用 - 对于不支持 SMIL 动画 (IE...) 的浏览器,您无法回退到基于脚本的动画。
这两个限制都不影响在 <object>
、<embed>
或 <iframe>
中加载的 SVG,因此如果需要,您可以改用它。
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.
CSS 动画 <img>
.
就像 SMIL 动画一样,它们应该在支持浏览器的情况下工作,具有相同的 user-gesture 限制和相同的可能方式(使用其他容器):
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>
中基于脚本的动画。
这些根本行不通。无法编写嵌入在 <img>
标记中的 SVG 文档。
要解决此问题,请使用 <object>
、<embed>
或 <iframe>
元素作为容器。
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>
所以基本上,<img>
中的 SVG 有很多限制,使用其他容器都可以克服这些限制。
现在,每个容器都有自己的限制:
<iframe>
不会根据其内容调整大小,它还会默认带有边框和其他一些丑陋的东西。<object>
和<embed>
将在不可见时被 webkit 浏览器卸载 (display: none
),并且不会在任何浏览器中缓存...
当然,也可以通过 AJAX 获取您的 SVG 标记并将其加载到您的实际 HTML 页面中,但我个人不建议这样做:
- 您需要确保没有重复的 id 元素,
- 您需要确保所有 CSS 规则都足够具体,不会影响页面中的其他元素,
- 您必须确保只加载受信任的代码,因为脚本会 运行,也就是说您对 XSS 攻击敞开大门。
既然我们在这里,<img>
中 SVG 的另一个限制是它无法加载其自身标记之外的任何资源,所有内容都需要直接包含在其中,甚至是字体和光栅图像.