我想通过JavaScript将SVG转换为位图图像(如JPEG, PNG等)。
下面是你如何通过JavaScript来做到这一点:
使用Canvas JavaScript库渲染SVG图像:https://github.com/gabelerner/canvg 根据以下说明,从画布中捕获编码为JPG(或PNG)的数据URI:
Jbeard4解决方案工作得很好。
我使用Raphael SketchPad创建SVG。链接到步骤1中的文件。
对于保存按钮(svg的id是"editor", canvas的id是"canvas"):
$("#editor_save").click(function() {
// the canvg call that takes the svg xml and converts it to a canvas
canvg('canvas', $("#editor").html());
// the canvas calls to output a png
var canvas = document.getElementById("canvas");
var img = canvas.toDataURL("image/png");
// do what you want with the base64, write to screen, post to server, etc...
});
这似乎在大多数浏览器中都有效:
function copyStylesInline(destinationNode, sourceNode) {
var containerElements = ["svg","g"];
for (var cd = 0; cd < destinationNode.childNodes.length; cd++) {
var child = destinationNode.childNodes[cd];
if (containerElements.indexOf(child.tagName) != -1) {
copyStylesInline(child, sourceNode.childNodes[cd]);
continue;
}
var style = sourceNode.childNodes[cd].currentStyle || window.getComputedStyle(sourceNode.childNodes[cd]);
if (style == "undefined" || style == null) continue;
for (var st = 0; st < style.length; st++){
child.style.setProperty(style[st], style.getPropertyValue(style[st]));
}
}
}
function triggerDownload (imgURI, fileName) {
var evt = new MouseEvent("click", {
view: window,
bubbles: false,
cancelable: true
});
var a = document.createElement("a");
a.setAttribute("download", fileName);
a.setAttribute("href", imgURI);
a.setAttribute("target", '_blank');
a.dispatchEvent(evt);
}
function downloadSvg(svg, fileName) {
var copy = svg.cloneNode(true);
copyStylesInline(copy, svg);
var canvas = document.createElement("canvas");
var bbox = svg.getBBox();
canvas.width = bbox.width;
canvas.height = bbox.height;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, bbox.width, bbox.height);
var data = (new XMLSerializer()).serializeToString(copy);
var DOMURL = window.URL || window.webkitURL || window;
var img = new Image();
var svgBlob = new Blob([data], {type: "image/svg+xml;charset=utf-8"});
var url = DOMURL.createObjectURL(svgBlob);
img.onload = function () {
ctx.drawImage(img, 0, 0);
DOMURL.revokeObjectURL(url);
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob)
{
var blob = canvas.msToBlob();
navigator.msSaveOrOpenBlob(blob, fileName);
}
else {
var imgURI = canvas
.toDataURL("image/png")
.replace("image/png", "image/octet-stream");
triggerDownload(imgURI, fileName);
}
document.removeChild(canvas);
};
img.src = url;
}
更改SVG以匹配您的元素
function svg2img(){
var svg = document.querySelector('svg');
var xml = new XMLSerializer().serializeToString(svg);
var svg64 = btoa(xml); //for utf8: btoa(unescape(encodeURIComponent(xml)))
var b64start = 'data:image/svg+xml;base64,';
var image64 = b64start + svg64;
return image64;
};svg2img()
我最近发现了几个用于JavaScript的图像跟踪库,它们确实能够构建一个可接受的位图近似值,包括大小和质量。我正在开发这个JavaScript库和CLI:
https://www.npmjs.com/package/svg-png-converter
它为所有这些提供了统一的API,支持浏览器和节点,不依赖于DOM,以及命令行工具。
对于转换标志/卡通/喜欢的图像,它做得很好。对于照片/现实主义,需要一些调整,因为输出大小可以增长很多。
它有一个游乐场,虽然现在我正在做一个更好的,更容易使用,因为添加了更多的功能:
https://cancerberosgx.github.io/demos/svg-png-converter/playground/#
我的用例是从网络加载svg数据,这个ES6类做了这项工作。
class SvgToPngConverter {
constructor() {
this._init = this._init.bind(this);
this._cleanUp = this._cleanUp.bind(this);
this.convertFromInput = this.convertFromInput.bind(this);
}
_init() {
this.canvas = document.createElement("canvas");
this.imgPreview = document.createElement("img");
this.imgPreview.style = "position: absolute; top: -9999px";
document.body.appendChild(this.imgPreview);
this.canvasCtx = this.canvas.getContext("2d");
}
_cleanUp() {
document.body.removeChild(this.imgPreview);
}
convertFromInput(input, callback) {
this._init();
let _this = this;
this.imgPreview.onload = function() {
const img = new Image();
_this.canvas.width = _this.imgPreview.clientWidth;
_this.canvas.height = _this.imgPreview.clientHeight;
img.crossOrigin = "anonymous";
img.src = _this.imgPreview.src;
img.onload = function() {
_this.canvasCtx.drawImage(img, 0, 0);
let imgData = _this.canvas.toDataURL("image/png");
if(typeof callback == "function"){
callback(imgData)
}
_this._cleanUp();
};
};
this.imgPreview.src = input;
}
}
下面是你如何使用它
let input = "https://restcountries.eu/data/afg.svg"
new SvgToPngConverter().convertFromInput(input, function(imgData){
// You now have your png data in base64 (imgData).
// Do what ever you wish with it here.
});
如果你想要一个普通的JavaScript版本,你可以访问Babel网站并在那里编译代码。
解决方案转换SVG到blob URL和blob URL到png图像
const svg=`<svg version="1.1" baseProfile="full" width="300" height="200" xmlns="http://www.w3.org/2000/svg"> <rect width="100%" height="100%" fill="red" /> <circle cx="150" cy="100" r="80" fill="green" /> <text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text></svg>` svgToPng(svg,(imgData)=>{ const pngImage = document.createElement('img'); document.body.appendChild(pngImage); pngImage.src=imgData; }); function svgToPng(svg, callback) { const url = getSvgUrl(svg); svgUrlToPng(url, (imgData) => { callback(imgData); URL.revokeObjectURL(url); }); } function getSvgUrl(svg) { return URL.createObjectURL(new Blob([svg], { type: 'image/svg+xml' })); } function svgUrlToPng(svgUrl, callback) { const svgImage = document.createElement('img'); // imgPreview.style.position = 'absolute'; // imgPreview.style.top = '-9999px'; document.body.appendChild(svgImage); svgImage.onload = function () { const canvas = document.createElement('canvas'); canvas.width = svgImage.clientWidth; canvas.height = svgImage.clientHeight; const canvasCtx = canvas.getContext('2d'); canvasCtx.drawImage(svgImage, 0, 0); const imgData = canvas.toDataURL('image/png'); callback(imgData); // document.body.removeChild(imgPreview); }; svgImage.src = svgUrl; }
Svg到png可以根据条件转换:
如果svg的格式是svg(字符串)路径:
创建画布 创建新的Path2D()并设置svg为参数 在画布上绘制路径 创建图像并使用canvas.toDataURL()作为src。
例子:
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
let svgText = 'M10 10 h 80 v 80 h -80 Z';
let p = new Path2D('M10 10 h 80 v 80 h -80 Z');
ctx.stroke(p);
let url = canvas.toDataURL();
const img = new Image();
img.src = url;
注意,ie中不支持Path2D, edge中部分支持。填充物解决了这个问题: https://github.com/nilzona/path2d-polyfill
使用.drawImage()创建svg blob并在画布上绘制:
制作画布元素 从svg xml生成一个svgBlob对象 从domUrl.createObjectURL(svgBlob)创建一个url对象; 创建一个Image对象并将url分配给Image src 将图像绘制到画布中 从canvas获取png数据字符串:canvas. todataurl ();
漂亮的描述: https://web.archive.org/web/20200125162931/http://ramblings.mcpher.com:80/Home/excelquirks/gassnips/svgtopng
注意,在ie中,你会在canvas.toDataURL()的阶段得到异常;这是因为IE有很高的安全性限制,在那里绘制图像后将canvas视为只读。所有其他浏览器只限制图像是交叉来源。
使用canvg JavaScript库。它是一个单独的库,但有有用的功能。
如:
ctx.drawSvg(rawSvg);
var dataURL = canvas.toDataURL();
下面是一个函数,它在没有库的情况下工作,并返回一个Promise:
/**
* converts a base64 encoded data url SVG image to a PNG image
* @param originalBase64 data url of svg image
* @param width target width in pixel of PNG image
* @return {Promise<String>} resolves to png data url of the image
*/
function base64SvgToBase64Png (originalBase64, width) {
return new Promise(resolve => {
let img = document.createElement('img');
img.onload = function () {
document.body.appendChild(img);
let canvas = document.createElement("canvas");
let ratio = (img.clientWidth / img.clientHeight) || 1;
document.body.removeChild(img);
canvas.width = width;
canvas.height = width / ratio;
let ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
try {
let data = canvas.toDataURL('image/png');
resolve(data);
} catch (e) {
resolve(null);
}
};
img.onerror = function() {
resolve(null);
};
img.src = originalBase64;
});
}
在Firefox上,svg没有设置宽度/高度是有问题的。
请参阅此工作示例,其中包括Firefox问题的修复。
使用Canvg库有几种方法将SVG转换为PNG。
在我的例子中,我需要从内联SVG中获取PNG blob。
库文档提供了一个示例(参见OffscreenCanvas示例)。
但是这个方法目前在Firefox中不起作用。是的,你可以在设置中启用gfx. offscreenvas .enabled选项。但是网站上的每个用户都会这么做吗?:)
然而,还有另一种方法也适用于Firefox。
const el = document.getElementById("some-svg"); //this is our inline SVG
var canvas = document.createElement('canvas'); //create a canvas for the SVG render
canvas.width = el.clientWidth; //set canvas sizes
canvas.height = el.clientHeight;
const svg = new XMLSerializer().serializeToString(el); //convert SVG to string
//render SVG inside canvas
const ctx = canvas.getContext('2d');
const v = await Canvg.fromString(ctx, svg);
await v.render();
let canvasBlob = await new Promise(resolve => canvas.toBlob(resolve));
最后一行多亏了这个答案
这是我的2美分。不知怎的,下载锚标签在代码片段中不像预期的那样工作,但在Chrome中工作得很好。
这里正在使用jsFiddle
const waitForImage = imgElem => new Promise(resolve => imgElem.complete ? resolve() : imgElem.onload = imgElem.onerror = resolve); const svgToImgDownload = ext => { if (!['png', 'jpg', 'webp'].includes(ext)) return; const _svg = document.querySelector("#svg_container").querySelector('svg'); const xmlSerializer = new XMLSerializer(); let _svgStr = xmlSerializer.serializeToString(_svg); const img = document.createElement('img'); img.src = 'data:image/svg+xml;base64,' + window.btoa(_svgStr); waitForImage(img) .then(_ => { const canvas = document.createElement('canvas'); canvas.width = _svg.clientWidth; canvas.height = _svg.clientHeight; canvas.getContext('2d').drawImage(img, 0, 0, _svg.clientWidth, _svg.clientHeight); return canvas.toDataURL('image/' + (ext == 'jpg' ? 'jpeg' : ext), 1.0); }) .then(dataURL => { console.log(dataURL); document.querySelector("#img_download_btn").innerHTML = `<a href="${dataURL}" download="img.${ext}">Download</a>`; }) .catch(console.error); }; document.querySelector('#map2Png').addEventListener('click', _ => svgToImgDownload('png')); document.querySelector('#map2Jpg').addEventListener('click', _ => svgToImgDownload('jpg')); document.querySelector('#map2Webp').addEventListener('click', _ => svgToImgDownload('webp')); <div id="svg_container" style="float: left; width: 50%"> <svg width="200" height="200" viewBox="-100 -100 200 200"> <circle cx="0" cy="20" r="70" fill="#D1495B" /> <circle cx="0" cy="-75" r="12" fill="none" stroke="#F79257" stroke-width="2" /> <rect x="-17.5" y="-65" width="35" height="20" fill="#F79257" /> </svg> </div> <div> <button id="map2Png">PNG</button> <button id="map2Jpg">JPG</button> <button id="map2Webp">WEBP</button> </div> <div id="img_download_btn"></div>
这是一个老问题,在2022年我们有ES6,我们不需要第三方库。
下面是将svg图像转换为其他格式的一种非常基本的方法。
诀窍是将svg元素作为img元素加载,然后使用canvas元素将图像转换为所需的格式。因此,需要四个步骤:
提取svg作为xml数据字符串。 将xml数据字符串加载到img元素中 使用canvas元素将img元素转换为dataURL 将转换后的dataURL加载到新的img元素中
步骤1
提取svg as xml数据字符串很简单,我们不需要将其转换为base64字符串。我们只是将其序列化为XML,然后将字符串编码为URI:
// Select the element:
const $svg = document.getElementById('svg-container').querySelector('svg')
// Serialize it as xml string:
const svgAsXML = (new XMLSerializer()).serializeToString($svg)
// Encode it as a data string:
const svgData = `data:image/svg+xml,${encodeURIComponent(svgAsXML)}`
步骤2
将xml数据字符串加载到img元素中:
// This function returns a Promise whenever the $img is loaded
const loadImage = async url => {
const $img = document.createElement('img')
$img.src = url
return new Promise((resolve, reject) => {
$img.onload = () => resolve($img)
$img.onerror = reject
$img.src = url
})
}
步骤3
使用canvas元素将img元素转换为dataURL:
const $canvas = document.createElement('canvas')
$canvas.width = $svg.clientWidth
$canvas.height = $svg.clientHeight
$canvas.getContext('2d').drawImage(img, 0, 0, $svg.clientWidth, $svg.clientHeight)
return $canvas.toDataURL(`image/${format}`, 1.0)
步骤4
将转换后的dataURL加载到新的img元素中:
const $img = document.createElement('img')
$img.src = dataURL
$holder.appendChild($img)
这里你有一个工作片段:
const $svg = document.getElementById('svg-container').querySelector('svg') const $holder = document.getElementById('img-container') const $label = document.getElementById('img-format') const destroyChildren = $element => { while ($element.firstChild) { const $lastChild = $element.lastChild ?? false if ($lastChild) $element.removeChild($lastChild) } } const loadImage = async url => { const $img = document.createElement('img') $img.src = url return new Promise((resolve, reject) => { $img.onload = () => resolve($img) $img.onerror = reject }) } const convertSVGtoImg = async e => { const $btn = e.target const format = $btn.dataset.format ?? 'png' $label.textContent = format destroyChildren($holder) const svgAsXML = (new XMLSerializer()).serializeToString($svg) const svgData = `data:image/svg+xml,${encodeURIComponent(svgAsXML)}` const img = await loadImage(svgData) const $canvas = document.createElement('canvas') $canvas.width = $svg.clientWidth $canvas.height = $svg.clientHeight $canvas.getContext('2d').drawImage(img, 0, 0, $svg.clientWidth, $svg.clientHeight) const dataURL = await $canvas.toDataURL(`image/${format}`, 1.0) console.log(dataURL) const $img = document.createElement('img') $img.src = dataURL $holder.appendChild($img) } const buttons = [...document.querySelectorAll('[data-format]')] for (const $btn of buttons) { $btn.onclick = convertSVGtoImg } .wrapper { display: flex; flex-flow: row nowrap; width: 100vw; } .images { display: flex; flex-flow: row nowrap; width: 70%; } .image { width: 50%; display: flex; flex-flow: row wrap; justify-content: center; } .label { width: 100%; text-align: center; } <div class="wrapper"> <div class="item images"> <div class="image left"> <div class="label">svg</div> <div id="svg-container"> <svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="200" height="200" viewBox="0 0 248 204"> <path fill="#1d9bf0" d="M221.95 51.29c.15 2.17.15 4.34.15 6.53 0 66.73-50.8 143.69-143.69 143.69v-.04c-27.44.04-54.31-7.82-77.41-22.64 3.99.48 8 .72 12.02.73 22.74.02 44.83-7.61 62.72-21.66-21.61-.41-40.56-14.5-47.18-35.07 7.57 1.46 15.37 1.16 22.8-.87-23.56-4.76-40.51-25.46-40.51-49.5v-.64c7.02 3.91 14.88 6.08 22.92 6.32C11.58 63.31 4.74 33.79 18.14 10.71c25.64 31.55 63.47 50.73 104.08 52.76-4.07-17.54 1.49-35.92 14.61-48.25 20.34-19.12 52.33-18.14 71.45 2.19 11.31-2.23 22.15-6.38 32.07-12.26-3.77 11.69-11.66 21.62-22.2 27.93 10.01-1.18 19.79-3.86 29-7.95-6.78 10.16-15.32 19.01-25.2 26.16z"/> </svg> </div> </div> <div class="image right"> <div id="img-format" class="label"></div> <div id="img-container"></div> </div> </div> <div class="item buttons"> <button id="btn-png" data-format="png">PNG</button> <button id="btn-jpg" data-format="jpeg">JPG</button> <button id="btn-webp" data-format="webp">WEBP</button> </div> </div>
推荐文章
- 如何从javascript子类调用父方法?
- Javascript中Uint8Array的字符串
- 跨浏览器JavaScript(不是jQuery…)滚动到顶部动画
- 如何找到一个值的数组索引?
- 当内部元素滚动位置达到顶部/底部时,防止父元素的滚动?
- Node.js和CPU密集型请求
- val()和text()的区别
- 如何使用Jest测试对象键和值是否相等?
- 将长模板文字行换行为多行,而无需在字符串中创建新行
- 如何在JavaScript中映射/减少/过滤一个集?
- Bower: ENOGIT Git未安装或不在PATH中
- 添加javascript选项选择
- 在Node.js中克隆对象
- 为什么在JavaScript的Date构造函数中month参数的范围从0到11 ?
- 使用JavaScript更改URL参数并指定默认值