英文:
Convert animateTransform SVG's transformation into animated .gif image
问题
Is it possible to convert SVG transformation into an animated GIF image?
英文:
tl;dr Is it possible to "convert" SVG transformation into animated GIF image?
I have this simple loader, an animated gears that are using animateTransform
transfomation to "achieve its goals" (roate gears):
<?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>
Is there any way to "convert" this piece of XML code (SVG file) into animated .gif?
Services I have tried so far:
- cloudconvert.com: empty, single color, white image (see)
- convert.io: broken images (see)
- convertio.co: broken images (see)
- freeconvert.com: server-side error
- zamzar.com: empty, single color, non-white image (see)
I have also tried to simply record this animation, as a screencast, using LICEcap, but to no avail. Animation is very "choppy" even when screen-casting at 24 FPS and I have problem capturing the animation from first to last frame (to get correct "gluing" point), to achieve endless animation.
Can you shed some light here? Is converting SVG transformation into animated image possible?
EDIT: Since all described (and probably all other) on-line services fails to convert SVG transformation into a file then I assume this is a programming question. I need a console script or piece of programming language that will take the this SVG code (as is or modified) as an input and will produce an image as an output.
Unfortunately this makes such question probably too broad, because I have no knowledge to suggest which programming or script language can be used to achieve this task.
答案1
得分: 2
以下是您要翻译的内容:
"With some modifications to your SVG you can use the following code to generate a series of images. The images can then be downloaded and converted to a Gif using a tool like ImageMagick (you need something like this command: how to make a high quality animated image with imagemagick - Stack Overflow).
You can see that I changed the SVG a bit. So, this script will only work if the animateTransform element is a child of the element that needs to be animated (so get rid of the ids and the href attribute). In your case, the animateTransform element can probably (I didn't test and it doesn't work here in SO) be a child of the image element, but all files need to run from the same web server because of cross-origin issues. An alternative approach could be to embed the gear1, 2 and 3 SVGs into the main SVG (and then let the animateTransform element be a child of each svg element (yes, embed SVG elements directly in the main SVG)).
The script generates a lot of links. Each link refers to an image. Right-click the link and select "Save Link as..." and save all the images in a directory. You probably do not need all of them now that a gear doesn't need a full rotation to make an infinite loop gif."
希望这对您有所帮助。
英文:
With some modifications to your SVG you can use the following code to generate a series of images. The images can then be downloaded and converted to a Gif using a tool like ImageMagick (you need something like this command: how to make a high quality animated image with imagemagick - Stack Overflow).
You can see that I changed the SVG a bit. So, this script will only work if the animateTransform element is a child of the element that needs to be animated (so get rid of the ids and the href attribute). In you case the animateTransform element can probably (I didn't test and it doesn't work here in SO) be a child of the image element, but all files need to run from the same webserver because of cross origin issues. An alternative approch could be to embed the gear1, 2 and 3 SVGs into the main SVG (and then let the animateTransform element be a child of each svg element (yes, embed SVG elements directly in the main SVG)).
The script generates a lot of links. Each link refer to an image. Right click the link and select "Save Link as..." and save all the images in a directory. You probably do not need all of them now that a gear don't need a full rotation to make a infinite loop gif.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
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);
});
});
<!-- language: lang-html -->
<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>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论