Base64 SVG images - javascript

I cannot get a base64 data URI for and SVG to appear as an image.
I tried an <img> and a <canvas> and for neither one does the SVG show up.
var url = 'data:image/svg+xml;base64,' + btoa('<svg height="100" width="100"><circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red"/></svg>');
document.getElementById('image').src = url;
var context = document.getElementById('canvas').getContext('2d');
var image = new Image();
image.src = url;
context.drawImage(image, 0, 0);
canvas, img {
border: 1px solid black;
}
<img id="image" width="200" height="200">
<canvas id="canvas" width="200" height="200"></canvas>
Tested in Chrome and Firefox.
What doesn't the SVG show up?

When embeding svg like this, remember to set xmlns for the svg:
var url = 'data:image/svg+xml;base64,' +
btoa('<svg xmlns="http://www.w3.org/2000/svg" height="100" width="100"><circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red"/></svg>');
If there is any xlink prefix used in your svg elements, you should also add xmlns:xlink="http://www.w3.org/1999/xlink"

Related

SVG with border or padding style is cropped when apply on image element

const svgStr = document.querySelector("svg").outerHTML,
svgBlob = new Blob([svgStr], { type: "image/svg+xml;charset=utf-8" }),
imgElem = new Image();
imgElem.src = URL.createObjectURL(svgBlob);
document.body.appendChild(imgElem);
<div>inline svg</div>
<svg width="300" height="300" viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg"
style="border:5px solid red;">
</svg>
<br> <br>
<div>svg as image content</div>
I try to convert svg string to image element. It has 5px border.
when I add this svg in inline html, svg don't crop but
when I try to load svg as image then svg crop.
It is only visible when scale down, apply ( transform:scale(0.9)).
I try to adjust svg width, height and viewbox but failed.
Then I try to set height and width on image element
imgElem.width = 310;
imgElem.height = 310;
But again,no success.
Output image:
Expected result:
How to fix this issue? Thanks in advance
Then as a solution - use outline and its offset to put graphics inside svg instead of real border.
outline:5px solid #F008;
outline-offset:-5px;
Disadvantages - may cover some svg content and if there is need of "pushing" contents away from outline drawnings then svg viewBox params should be edited. (added green ring for demo)
const svgStr = document.querySelector("svg").outerHTML,
svgBlob = new Blob([svgStr], { type: "image/svg+xml;charset=utf-8" }),
imgElem = new Image();
imgElem.src = URL.createObjectURL(svgBlob);
document.body.appendChild(imgElem);
<div>inline svg</div>
<svg width="300" height="300" viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg"
style="outline:5px solid #F008;outline-offset:-5px;">
<g stroke="green" fill="white" stroke-width="2">
<circle cx="150" cy="150" r="148"/>
</g>
</svg>
<br> <br>
<div>svg as image content</div>

Is there a way to change the image(base64) fill of an svg path useing js?

I'm generating an image using canvas. I would like to use that image to fill an SVG path background. Only part of the SVG.
I have tried:
<path id="path1" class="st1" d="M80.2,43.1C74,37.8,67.9,32.5,61.7,27.2c1.6-3.6,3.2-7.1,4.8-10.7C71,25.3,75.6,34.2,80.2,43.1z"/>
document.getElementById("path1").setAttribute('fill', "url('" + myCanvas.toDataURL() + "')");
also:
<pattern id="img1" patternUnits="userSpaceOnUse" width="100" height="100">
<image id="bg1" href="" x="0" y="0" width="100" height="100" />
<path id="path1" class="st1" d="M80.2,43.1C74,37.8,67.9,32.5,61.7,27.2c1.6-3.6,3.2-7.1,4.8-10.7C71,25.3,75.6,34.2,80.2,43.1z"fill="url(#img)/>
document.getElementById("bg1").setAttribute('href', "url('" + myCanvas.toDataURL() + "')");
I have tried with and without URL() and with and without quotes.
Something like this?
I'm pulling from how to use base64 data in an img tag, how to fill an svg path with a background image, and this SO Post for downloading images as base64 text.
b64url = "https://upload.wikimedia.org/wikipedia/commons/thumb/b/b6/Pencil_drawing_of_a_girl_in_ecstasy.jpg/800px-Pencil_drawing_of_a_girl_in_ecstasy.jpg"
function toDataURL(url, callback) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
var reader = new FileReader();
reader.onloadend = function() {
callback(reader.result);
}
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.send();
}
toDataURL(b64url, function(dataUrl) {
document.querySelector("#demoimg").src = dataUrl;
el = document.querySelector("#imgtag");
el.setAttribute("href", dataUrl);
});
<image id="demoimg" width="100" />
<br>
<br>
<?xml version="1.0" encoding="UTF-8"?>
<svg version="1.1"
baseProfile="full"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ev="http://www.w3.org/2001/xml-events">
<title>Text Pattern Fill Example</title>
<defs>
<pattern id="img1" patternUnits="userSpaceOnUse" width="100" height="100">
<image id="imgtag" x="0" y="0" width="100" height="100" />
</pattern>
</defs>
<path d="M5,50
l0,100 l100,0 l0,-100 l-100,0
M215,100
a50,50 0 1 1 -100,0 50,50 0 1 1 100,0
M265,50
l50,100 l-100,0 l50,-100
z"
fill="url(#img1)" />
</svg>

Render a single SVG element to canvas or image

How can I render a single SVG element (as opposed to the whole SVG document or parts of it) to an image or a canvas?
I have an SVG document with a lot of nodes. If I render the whole SVG or a part of it, the resulting image will contain other pieces of graphics I'm not interested in. I am interested in one specific SVG element and would like to render all of it without anything else.
Example:
<!DOCTYPE html>
<html>
<body>
<svg height="150" width="150">
<circle cx="50" cy="50" r="25" stroke="green" stroke-width="3" fill="gray" />
<circle id="IWantToRenderThis" cx="75" cy="75" r="25" stroke="red" stroke-width="3" fill="white" />
<circle cx="100" cy="100" r="25" stroke="blue" stroke-width="3" fill="black" />
</svg>
<script>
const target = document.getElementById("IWantToRenderThis");
// how can I render *target* into a texture?
</script>
</body>
</html>
How can I render target alone? I would like to obtain an image which has no traces of the other two circles, a transparent background, and the right size to fit target perfectly.
You could remove all elements but the one you want to render.
To fit the SVG to the remaining element, you need to calculate a new position and size. For your example the code could look like this:
<!DOCTYPE html>
<html>
<body>
<svg height="150" width="150" id="svg">
<circle cx="50" cy="50" r="25" stroke="green" stroke-width="3" fill="gray" />
<circle id="IWantToRenderThis" cx="75" cy="75" r="25" stroke="red" stroke-width="3" fill="white" />
<circle cx="100" cy="100" r="25" stroke="blue" stroke-width="3" fill="black" />
</svg>
<script>
const svg = document.getElementById("svg");
const target = document.getElementById("IWantToRenderThis");
const children = svg.children;
// Remove all child elements but the target
for(let index = 0; index < children.length; index++) {
const child = children[index];
if(child.id !== 'IWantToRenderThis') {
child.remove()
}
}
// Recalculate element position and svg size
const targetSize = parseInt(target.getAttribute('r'))
const targetStroke = parseInt(target.getAttribute('stroke-width'))
target.setAttribute('cx', targetSize + (targetStroke/2))
target.setAttribute('cy', targetSize + (targetStroke/2))
svg.setAttribute('width', targetSize*2 + targetStroke)
svg.setAttribute('height', targetSize*2 + targetStroke)
</script>
</body>
</html>
Note that you have to include the stroke width of your element to properly calculate its new position and the SVG's new size.
Here the target element is just copied using outerHTML into a new data URL representing the new SVG, loaded into an image object, drawn in a canvas and exported as a PNG image.
let img = document.getElementById('img');
let target = document.getElementById("IWantToRenderThis");
let svg = target.closest('svg');
let width = svg.attributes['width'].value;
let height = svg.attributes['height'].value;
let image = new Image();
let canvas = document.getElementById('canvas');
canvas.height = height;
canvas.width = width;
let ctx = canvas.getContext('2d');
image.addEventListener('load', e => {
ctx.drawImage(e.target, 0, 0, e.target.width, e.target.height);
img.src = canvas.toDataURL("image/png");
});
image.src = `data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg"
width="${width}" height="${height}">${target.outerHTML}</svg>`;
<p>Original SVG:</p>
<svg id="svg" height="150" width="150">
<circle cx="50" cy="50" r="25" stroke="green" stroke-width="3" fill="gray" />
<circle id="IWantToRenderThis" cx="75" cy="75" r="25" stroke="red" stroke-width="3" fill="white" />
<circle cx="100" cy="100" r="25" stroke="blue" stroke-width="3" fill="black" />
</svg>
<p>SVG rendered in canvas:</p>
<canvas id="canvas"></canvas>
<p>PNG image based on canvas:</p>
<img id="img" />

How to animate a mask svg from js?

How to animate using js mask, and masked the layer and layer mask svg? Without using third-party libraries?
<svg id="mask-layer" width="200" height="50" >
<defs>
<mask id="mask">
<rect width="200" height="50" style="fill: red" />
</mask>
</defs>
<rect id="masked" width="200" height="50" style="fill: red"/>
</svg>
Need to make a moving mask.
Was looking for an answer all day and is desperate. I will be glad to any advice on the topic.
You can draw one rect over another.
HTML:
<svg id="mask-layer" width="200" height="50" >
<rect id="masked" width="200" height="50" style="fill: blue"/>
<rect id='mask' width="0" height="50" style="fill: red" />
</svg>
<br />
<span id='progress'></span>
Javascript:
(function() {
var mask = document.getElementById('mask');
var progressSpan = document.getElementById('progress');
var fullWidth = 200;
var curWidth = mask.getAttribute('width');
var calculateProgress = function() {
var progress = (curWidth*100)/fullWidth;
progressSpan.innerHTML = ['Progress: ', Math.floor(progress), '%'].join('');
}
var interval = setInterval(function() {
mask.setAttribute('width', ++curWidth);
calculateProgress();
if(curWidth > fullWidth) {
clearInterval(interval);
progressSpan.innerHTML = 'Finished!';
}
}, 50);
})();
Here you have working example: https://jsfiddle.net/L1vy2t3o/
Using svg mask, you can do something like this:
(function() {
var btnMove = document.getElementById("btnMove");
var rect = document.getElementById("rect");
var pos = 100;
btnMove.addEventListener("click", function() {
rect.setAttribute("x", pos);
pos += 100;
});
})();
<svg id="mask-layer" width="200" height="50">
<defs>
<mask id="mask" style="stroke: #9595C6;">
<rect width="200" height="50" />
</mask>
</defs>
<rect id="rect" x="0" y="0" width="200" height="50" style="fill: #9595C6;" />
<rect x="0" y="0" width="200" height="50" mask="url(#mask)" />
</svg>
<br />
<button id="btnMove">Move</button>

How to apply canvg() function for particular div?

I am using canvg() function to convert svg into canvas.
If we use canvg() directly on onload it will convert all svg to canvas.
I wanted to convert svg related to particular div.
Html
<div id="notapply">
<svg><text x="50" y="50">Not to Apply!</text></svg>
</div>
<div id="apply">
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
</svg>
</div>
Script
canvg();
Here it should convert svg related to div which is having id=apply.
Fiddle demo here
I have found an answer on your question in the source code of canvg itself: canvg
You need to change query selector to select SVG from your div:
// Your selector here
var svgTags = document.querySelectorAll('#apply svg');
// Process SVG tags
for (var i=0; i<svgTags.length; i++) {
var svgTag = svgTags[i];
var c = document.createElement('canvas');
c.width = svgTag.clientWidth;
c.height = svgTag.clientHeight;
svgTag.parentNode.insertBefore(c, svgTag);
svgTag.parentNode.removeChild(svgTag);
var div = document.createElement('div');
div.appendChild(svgTag);
canvg(c, div.innerHTML);
}
Here is your example modified: http://jsfiddle.net/ischenkodv/L3hondLn/135/
You can use XMLSerializer to serialize the SVG you want to send to canvg.
Something like this perhaps...
canvg("canvas", (new XMLSerializer).serializeToString(document.getElementById("apply").firstElementChild));
<script src="https://cdnjs.cloudflare.com/ajax/libs/canvg/1.5/canvg.min.js"></script>
<div id="notapply">
<svg><text x="50" y="50">Not to Apply!</text></svg>
</div>
<div id="apply">
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
</svg>
</div>
<canvas id="canvas" width="500" height="500"></canvas>

Categories

Resources