tl;dr 是否可以将 SVG 转换“转换”为动画 GIF 图像?
我有这个简单的加载器,一个动画齿轮,它使用
animateTransform
转换来“实现其目标”(旋转齿轮):
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg version="1.1" baseProfile="full" width="213" height="180" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<image id="gear1" x="0" y="0" width="92" height="92" xlink:href="gear1.svg"/>
<image id="gear2" x="57.5" y="57.5" width="122" height="122" xlink:href="gear2.svg" transform="rotate(16.875 118.5 118.5)"/>
<image id="gear3" x="149" y="24" width="64" height="64" xlink:href="gear3.svg"/>
<animateTransform xlink:href="#gear1" attributeName="transform" attributeType="XML" type="rotate" from="0 46 46" to="360 46 46" dur="6s" repeatCount="indefinite"/>
<animateTransform xlink:href="#gear2" attributeName="transform" attributeType="XML" type="rotate" to="16.875 118.5 118.5" from="376.875 118.5 118.5" dur="8s" repeatCount="indefinite"/>
<animateTransform xlink:href="#gear3" attributeName="transform" attributeType="XML" type="rotate" from="0 181 56" to="360 181 56" dur="4s" repeatCount="indefinite"/>
</svg>
有没有办法将这段XML代码(SVG文件)“转换”成动画.gif?
到目前为止我尝试过的服务:
我想为它提供开发人员的方法,但使用代码(控制台/Javascript 脚本)将此 SVG 代码转换为一个图像或一系列图像 - 如果可能的话。
对 SVG 进行一些修改后,您可以使用以下代码生成一系列图像。然后可以使用 ImageMagick 等工具下载图像并将其转换为 Gif(您需要类似以下命令:如何使用 imagemagick 制作高质量的动画图像 - Stack Overflow)。
你可以看到我对 SVG 做了一些更改。因此,只有当 animateTransform 元素是需要动画的元素的子元素时,此脚本才有效(因此请去掉 ids 和 href 属性)。在你的情况下, animateTransform 元素可能(我没有测试,它在这里不起作用)是图像元素的子元素,但由于跨源问题,所有文件都需要从同一个网络服务器运行。另一种方法是将 gear1、2 和 3 SVG 嵌入到主 SVG 中(然后让 animateTransform 元素成为每个 svg 元素的子元素(是的,将 SVG 元素直接嵌入到主 SVG 中)。
该脚本生成大量链接。每个链接都引用一个图像。右键单击该链接并选择“链接另存为...”并将所有图像保存在一个目录中。现在你可能不需要所有这些,因为齿轮不需要完全旋转来制作无限循环 gif。
var svgcontainer, svg, canvas, ctx, output, interval;
var num = 101;
const nsResolver = prefix => {
var ns = {
'svg': 'http://www.w3.org/2000/svg',
'xlink': 'http://www.w3.org/1999/xlink'
};
return ns[prefix] || null;
};
const takeSnap = function() {
// get all animateTransform elements
let animateXPath = document.evaluate('//svg:*[svg:animateTransform]', svg, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
// store all animateTransform animVal.matrix in a dataset attribute
Object.keys([...Array(animateXPath.snapshotLength)]).forEach(i => {
let node = animateXPath.snapshotItem(i);
let mStr = [...node.transform.animVal].map(animVal => {
let m = animVal.matrix;
return `matrix(${m.a} ${m.b} ${m.c} ${m.d} ${m.e} ${m.f})`;
}).join(' ');
node.dataset.transform = mStr;
});
// get all animate elements
animateXPath = document.evaluate('//svg:animate', svg, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
// store all animate properties in a dataset attribute on the target for the animation
Object.keys([...Array(animateXPath.snapshotLength)]).forEach(i => {
let node = animateXPath.snapshotItem(i);
let propName = node.getAttribute('attributeName');
let target = node.targetElement;
let computedVal = getComputedStyle(target)[propName];
target.dataset[propName] = computedVal;
});
// create a copy of the SVG DOM
let parser = new DOMParser();
let svgcopy = parser.parseFromString(svg.outerHTML, "application/xml");
// find all elements with a dataset attribute
animateXPath = svgcopy.evaluate('//svg:*[@*[starts-with(name(), "data")]]', svgcopy, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
// copy the animated property to a style or attribute on the same element
Object.keys([...Array(animateXPath.snapshotLength)]).forEach(i => {
let node = animateXPath.snapshotItem(i);
// for each data-
for (key in node.dataset) {
if (key == 'transform') {
node.setAttribute(key, node.dataset[key]);
} else {
node.style[key] = node.dataset[key];
}
}
});
// find all animate and animateTransform elements from the copy document
animateXPath = svgcopy.evaluate('//svg:*[starts-with(name(), "animate")]', svgcopy, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
// remove all animate and animateTransform elements from the copy document
Object.keys([...Array(animateXPath.snapshotLength)]).forEach(i => {
let node = animateXPath.snapshotItem(i);
node.remove();
});
// create a File object
let file = new File([svgcopy.rootElement.outerHTML], 'svg.svg', {
type: "image/svg+xml"
});
// and a reader
let reader = new FileReader();
reader.addEventListener('load', e => {
/* create a new image assign the result of the filereader
to the image src */
let img = new Image();
// wait got load
img.addEventListener('load', e => {
// update canvas with new image
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(e.target, 0, 0);
// create PNG image based on canvas
//let img = new Image();
//img.src = canvas.toDataURL("image/png");
//output.append(img);
let a = document.createElement('A');
a.textContent = `Image-${num}`;
a.href = canvas.toDataURL("image/png");
a.download = `Image-${num}`;
num++;
output.append(a);
});
img.src = e.target.result;
});
// read the file as a data URL
reader.readAsDataURL(file);
};
document.addEventListener('DOMContentLoaded', e => {
svgcontainer = document.getElementById('svgcontainer');
canvas = document.getElementById('canvas');
output = document.getElementById('output');
ctx = canvas.getContext('2d');
let parser = new DOMParser();
let svgdoc = parser.parseFromString(svgcontainer.innerHTML, "application/xml");
canvas.width = svgdoc.rootElement.getAttribute('width');
canvas.height = svgdoc.rootElement.getAttribute('height');
//svgcontainer.innerHTML = svgdoc.rootElement.outerHTML;
svg = svgcontainer.querySelector('svg');
//console.log(svg);
// set interval
interval = setInterval(takeSnap, 50);
// get all
let animateXPath = document.evaluate('//svg:*[starts-with(name(), "animate")]', svg, nsResolver, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
let animationArr = Object.keys([...Array(animateXPath.snapshotLength)]).map(i => {
let node = animateXPath.snapshotItem(i);
return new Promise((resolve, reject) => {
node.addEventListener('endEvent', e => {
resolve();
});
});
});
Promise.all(animationArr).then(value => {
clearInterval(interval);
});
});
<div style="display:flex">
<div id="svgcontainer">
<svg width="213" height="180" xmlns="http://www.w3.org/2000/svg">
<rect width="92" height="92" fill="orange">
<animateTransform attributeName="transform" attributeType="XML" type="rotate" from="0 46 46" to="360 46 46" dur="6s" repeatCount="1"/>
</rect>
<rect x="57.5" y="57.5" width="122" height="122" fill="green">
<animateTransform attributeName="transform" attributeType="XML" type="rotate" to="16.875 118.5 118.5" from="376.875 118.5 118.5" dur="8s" repeatCount="1"/>
</rect>
<rect x="149" y="24" width="64" height="64" fill="navy">
<animateTransform attributeName="transform" attributeType="XML" type="rotate" from="0 181 56" to="360 181 56" dur="4s" repeatCount="1"/>
</rect>
</svg>
</div>
<canvas id="canvas" width="200" height="200"></canvas>
</div>
<p>Exported PNGs:</p>
<div id="output"></div>