I'm trying to convert a bunch of siblings SVG images to base64 PNG strings, using just the browser and plain Javascript, but for some reason beyond my knowledge I get the base64 PNG string of only the last SVG. Here is the HTML snippet:
<html>
<head><title>Browser SVG to PNG Converter</title></head>
<body bgcolor="#DDDDEE">
<h1>Browser SVG to PNG Converter</h1>
<div id="div_svg">
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150">
<rect width="100%" height="100%" style="stroke-width:0;fill:rgb(35,235,235);" />
<rect x="30" y="15" width="90" height="120" style="stroke:#000000;stroke-width:1;fill:none;" /> </svg>
<svg xmlns="http://www.w3.org/2000/svg" width="130" height="150">
<rect width="100%" height="100%" style="stroke-width:0;fill:rgb(35,200,35);" />
<rect x="30" y="30" width="70" height="90" style="stroke:#000000;stroke-width:2;fill:none;" /> </svg>
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150">
<rect width="100%" height="100%" style="stroke-width:0;fill:rgb(35,235,235);" />
<rect x="30" y="15" width="90" height="120" style="stroke:#000000;stroke-width:3;fill:none;" /> </svg>
</div>
<br />
<button id="btn_convert">Convert SVG to PNG and display it below</button>
<br />
<br />
<canvas id="aux_canvas" style="display:none;"></canvas>
<textarea id="output_png" style="width:90vw;height:40vh;display:block;">base64 PNG source will be displayed here:</textarea>
<script>
document.getElementById('btn_convert').addEventListener('click', function() {
var svg_all = document.getElementById('div_svg').querySelectorAll('svg');
var canvas = document.getElementById('aux_canvas');
var win = window.URL || window.webkitURL || window;
var img = new Image();
img.addEventListener("load", function() {
canvas.getContext('2d').drawImage(img, 0, 0);
win.revokeObjectURL(url);
document.getElementById('output_png').value = "\n\n" canvas.toDataURL("image/png");
});
for(let i = 0; i < svg_all.length; i ) {
let svg = svg_all[i];
canvas.width = svg.getBoundingClientRect().width;
canvas.height = svg.getBoundingClientRect().height;
var data = new XMLSerializer().serializeToString(svg);
var blob = new Blob([data], {
type: 'image/svg xml'
});
var url = win.createObjectURL(blob);
document.getElementById('output_png').value = "\nGoing to load image..."
img.src = url;
document.getElementById('output_png').value = "\nEnded loading image..."
}
});
</script>
</body>
</html>
Input as hardcoded SVG and output to textarea
are only for testing purposes, my ultimate goal is a Javascript function getting an array of SVG sources and returning the corresponding PNG images as an array of base64 strings.
CodePudding user response:
Image loading is asynchronous, you have to wait for each image to load before moving to the next, use Promise with asyn
and await
before each loading.
Here is a working example
const convertButton = document.querySelector("#btn_convert");
const output = document.querySelector("#output_png");
const svgs = document.querySelectorAll("svg");
const img = document.createElement("img");
const canvas = document.createElement("canvas");
const ctx = canvas.getContext('2d');
const URL = window.URL || window.webkitURL || window;
convertButton.onclick = async () => {
for(let i = 0; i < svgs.length; i ) {
output.value = "\n---- Going to load image... \n\n";
output.value = await svgToBase64(svgs[i]);
output.value = "\n---- Ended loading image... \n\n";
}
};
async function svgToBase64(svg) {
const { width, height } = svg.getBoundingClientRect();
canvas.width = width;
canvas.height = height;
const data = new XMLSerializer().serializeToString(svg);
const blob = new Blob([data], {
type: 'image/svg xml'
});
var url = URL.createObjectURL(blob);
return new Promise((resolve) => {
img.onload = () => {
ctx.drawImage(img, 0, 0);
URL.revokeObjectURL(url);
resolve(canvas.toDataURL("image/png"));
}
img.src = url;
});
}
body {
background: #DDDDEE;
}
<h1>Browser SVG to PNG Converter</h1>
<div id="div_svg">
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150">
<rect width="100%" height="100%" style="stroke-width:0;fill:rgb(35,235,235);" />
<rect x="30" y="15" width="90" height="120" style="stroke:#000000;stroke-width:1;fill:none;" /> </svg>
<svg xmlns="http://www.w3.org/2000/svg" width="130" height="150">
<rect width="100%" height="100%" style="stroke-width:0;fill:rgb(35,200,35);" />
<rect x="30" y="30" width="70" height="90" style="stroke:#000000;stroke-width:2;fill:none;" /> </svg>
<svg xmlns="http://www.w3.org/2000/svg" width="150" height="150">
<rect width="100%" height="100%" style="stroke-width:0;fill:rgb(35,235,235);" />
<rect x="30" y="15" width="90" height="120" style="stroke:#000000;stroke-width:3;fill:none;" /> </svg>
</div>
<br />
<button id="btn_convert">Convert SVG to PNG and display it below</button>
<br />
<br />
<textarea id="output_png" style="width:90vw;height:40vh;display:block;">base64 PNG source will be displayed here:</textarea>