Create a SVG node from an external file with JavaScript - javascript

It is possible to synchronously create a <svg> element with Javascript that is a copy of a SVG file?
I have a small SVG in an external file called cursor.svg located in a folder called svg. So its relative path to my base HTML file is svg/cursor.svg. The SVG is very simple:
<svg id="cursor">
<rect x="50" y="20" rx="20" ry="10" width="10" height="150" style="fill:red;stroke-width:0" opacity="0.75">
<animate attributeName="opacity" begin="indefinite" values="0.75;0" dur="1s" repeatCount="1" />
</rect>
</svg>
I would like to be able to have a createSvgNode() function that simply returns a SVG node that is a copy of the one within cursor.svg. (so that the function code would remains the same even if the SVG file changes)
But I want it to be synchronous. Is there a possible solution for this? Maybe by loading the cursor.svg file in the HTML DOM?
Thanks for any suggestion.

Related

React and SVG sprite

I am using React, and I am trying to load a svg icon from a sprite. My sprite is like this:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol viewBox="0 0 28.3 28.3" id="square">
<path d="M.3 30.7h27L13.8 3.8zM126.3-51.7c-8.8 0-16 7.2-16 16s7.2 16 16 16 16-7.2 16-16-7.2-16-16-16z" />
<path d="M0 28.3h28.3L14.2 0 0 28.3zm5.3-3.2l8.9-17.7L23 25.1H5.3z" />
</symbol>
<symbol viewBox="0 0 28.3 28.3" id="circle">
<circle cx="14.2" cy="14.2" r="14.2" />
</symbol>
</defs>
</svg>
And I load it with:
<svg viewBox="0 0 28.3 28.3" className="App-icon">
<use xlinkHref="./sprite#square" />
</svg>
With no results. I made a sandbox as an example: https://codesandbox.io/s/l711v6j7v7
If you want to reference it as external resource you need to use the proper URL to the svg file and it needs to be publicly accessible. So in the codesandbox you need to move it to the public folder, so that you can access it in the browser via
https://codesandbox.io/s/l711v6j7v7/sprite.svg
Then you can reference it like this:
<use href="/sprite.svg#square" />
See this fork of your codesandbox.
For those where the SVG file is an existing/external svg file.
You probably have an existing SVG webpack loader which is not working with the concept of SVG sprites. Why? It generally needs a file reference/url to the sprite file or the SVG (nodes) must exist in the DOM (Solution below)
This works:
Transform the plain SVG to JSX (google html to jsx)
Create a new pure react component and simply return the transformed JSX in render() method
Import and include the created react sprite component
Now use the sprite symbol via <use><svg href="#symbolnameorid"></svg></use> You can use it without the file prefix now

How do I render svg images in React Native?

I've tried "react-native-remote-svg" and "react-native-svg-image"; neither of them manage to render the SVG file.
How do I handle SVG in React-native?
Example code:
import SVGImage from 'react-native-svg-image'
const EmailLogo = require('../static/others/Email.svg');
// Render etc....
<ButtonContainer>
<Button bgColor={colors.darkTeal} txtColor={colors.whiteText}
onPress={this.onSignInPress.bind(this)}>
LOG IN WITH
</Button>
<SVGImage
style={{ width: 80, height: 80 }}
source={EmailLogo}
/>
</ButtonContainer>
Result: It's a white square when it should be an email logo.
How does one handle SVG's properly in React-native?
I've been through this struggle. react-native-svg-icon helped me out, but there were some additional things that i had to do, to make it work.
First of all, this library uses react-native-svg underneath. And you need to convert your svg files into SVG objects that this library understands.
If your open your svg file with editor, it will look something like this
<svg xmlns="http://www.w3.org/2000/svg" viewBox="170.5 200.5 18.6 23">
<defs>
<style>.a{fill:#444;}.b{fill:#07b57a;}</style>
</defs>
<g transform="translate(171 201)">
<path class="a" d="M18.1,22.5H-.5V-.5H18.1ZM.5,21.5H17.1V.5H.5Z"/>
<rect class="b" width="5.4" height="1" transform="translate(9 5.4)"/>
<path class="b" d="M4.4,7.3,3,5.9l.7-.7.7.7L6.6,3.7l.7.7Z"/>
<rect class="b" width="5.4" height="1" transform="translate(9 10.5)"/>
<path class="b" d="M4.4,12.4,3,11l.7-.7.7.7L6.6,8.8l.7.7Z"/>
<rect class="b" width="5.4" height="1" transform="translate(9 15.6)"/>
<rect class="b" width="2.5" height="1" transform="translate(3.2 15.6)"/>
</g>
</svg>
You need to convert it to something like this
entry: {
svg: (
<G transform="translate(171 201)">
<Path fill="#444444" d="M-152.4-178H-171v-23h18.6V-178z M-170-179h16.6v-21H-170V-179z" />
<Rect x="-161.5" y="-195.1" fill="#07B57A" width="5.4" height="1" />
<Path
fill="#07B57A"
d="M-166.1-193.2l-1.4-1.4l0.7-0.7l0.7,0.7l2.2-2.2l0.7,0.7L-166.1-193.2z"
/>
<Rect x="-161.5" y="-190" fill="#07B57A" width="5.4" height="1" />
<Path
fill="#07B57A"
d="M-166.1-188.1l-1.4-1.4l0.7-0.7l0.7,0.7l2.2-2.2l0.7,0.7L-166.1-188.1z"
/>
<Rect x="-161.5" y="-184.9" fill="#07B57A" width="5.4" height="1" />
<Rect x="-167.3" y="-184.9" fill="#07B57A" width="2.5" height="1" />
</G>
),
viewBox: '0 0 18.6 23',
}
This is a representation of the svg file in components of react-native-svg library. One thing you need to pay attention here, is viewbox of the svg file. I am not sure why, but most of the time, it is 'off center'. I will show in screenshots below. Because of that, it cannot be displayed by the react-native-svg-icon as well. To bring it to center you can use Adobe Illustrator, or some other online tool to edit svg. One i used is http://editor.method.ac/. So, I uploaded my svg, recentered it and downloaded it again. and used that svg file to create object in my react native code.
This is my initial svg file that i uploaded to the service. if you zoom out and press cmd+a (or ctrl+a) to select all, it will highlight svg icon, like in screenshot below. You should position it to the white part, either by dragging it, or by setting X and Y on top right corner to 0s.
This is how it will look when centered
Once you save that svg file, use it to convert it to javascript object with react-native-svg components, more info on that can be found here
Once you create you svg objects, you can use it with react-native-svg-icon. You will find how to do that in the link I shared above.
I know, this is a lot of pain and seemingly over complicated, and I spent quite some time to make it work, but it is the only way I managed to accomplish it.
One other option would be to convert your svgs into font icons with icomoon.com and use it with react-native-vector-icons. but it will only work if your svgs are drawn with only one color, as multicolored ones cannot be converted to fonts
P.S. I didn't try, but maybe, libraries that you tried to use might work with centered svg file that we got from online service. Let me know if it works, then it can be helpful to other users as well.
react-native-svg-image and react-native-svg-image uses WebView to render SVG files so it do not support local files at the moment. Its written it the docs.
Use react-native-svg-uri to render SVG images in React Native from an URL or a static file. to use react-native-svg-uri you will need to link react-native-svg as well. So read docs carefully.

How to avoid shadow-root in MathJax?

I want to access the SVG code created by MathJax via javascript. Apparently, MathJax put the SVG <path> under shadow-root, which is not directly accessible by javascript. Here is a picture of the elements given by Chrome
If I get the <svg> element by any javascript method, the <path> children will not be included.
You cannot avoid shadow-root. It is not put there by MathJax renderer. It is because of the use tag.
From MDN:
The <use> element takes nodes from within the SVG document, and duplicates them somewhere else.
So MathJax creates svg pathes and gives them ids and reuses them. Say the letter a is rendered into svg, and MathJax stores it in the svg with an id and use it when the letter a is needed to be rendered.
Below example from MDN speaks better.
<svg width="80" height="80" xmlns="http://www.w3.org/2000/svg">
<style>
.classA {
fill: red;
}
</style>
<defs>
<g id="Port">
<circle style="fill: inherit;" r="10"/>
</g>
</defs>
<text y="15">black</text>
<use x="50" y="10" href="#Port" />
<text y="35">red</text>
<use x="50" y="30" href="#Port" class="classA"/>
<text y="55">blue</text>
<use x="50" y="50" href="#Port" style="fill: blue;"/>
</svg>

Dynamically add a shape / mask to an inline svg

I have a site with a series of elements that, when clicked on, add inline svg code to that element. The svg essentially animates an "iris wipe" to tunr the element white. Code from a separate html document is the loaded into a series of divs. When all the images are done loading, I want to append a mask to the svg code to iris wipe it back to how it was.
I am using waitForImages.js to check when the images are done loading. This is working successfully. The mask is also being added to the svg correctly. However, the mask animates.
Here is the initial code for adding the svg:
$("#selProject").append('<svg id="circleCont" x="0px" y="0px" viewBox="0 0 360 360" enable-background="new 0 0 360 360"><circle class="circ" cx="50%" cy="50%" r="0.01" stroke="#FFFFFF" stroke-width="0.5" fill="none"><animate attributeName="r" from="0.01" to="100%" dur="0.2" begin="0s" fill="freeze"/><animate attributeName="stroke-width" from="0.5" to="100" dur="0.2" begin="0s" fill="freeze"/></circle><circle class="circ" cx="50%" cy="50%" r="0.01" stroke="#FFFFFF" stroke-width="0.5" fill="none"><animate attributeName="r" from="0.01" to="100%" dur="0.2" begin="0.1s" fill="freeze"/><animate attributeName="stroke-width" from="0.5" to="200" dur="0.2" begin="0.1s" fill="freeze"/></circle><circle class="circ" cx="50%" cy="50%" r="0.01" fill="#FFFFFF" mask="url(#mask1)"><animate attributeName="r" from="0.01" to="100%" dur="0.3" begin="0.2s" fill="freeze"/></circle></svg>')
This is probably not the cleanest way to do it, but is the way I knew how.
Later, after some other code / loading the other html document using ajax
$("#selProject").waitForImages(function() {
$("#projectPageInfo").waitForImages(function() {
$("svg").append('<mask id="mask1"><rect fill="white" width="100%" height="100%" /><circle id="circmask" class="circ" cx="50%" cy="50%" r="0.01" fill="#000000"><animate attributeName="r" from="0.01" to="100%" dur="0.3" begin="0s" fill="freeze"/></circle></mask>');
});
});
The mask worked correctly when it was apart of the initial svg code being added, and animated as it should have. However, I needed it not to happen until the images have loaded, and now, despite the mask being added to the svg successfully, it does not animate. What am I missing?
I suspect it's the typical jQuery problem. JQuery cannot be relied on to do the right thing with SVG elements. jQuery is designed to work with HTML, not SVG whose elements are in a different namespace.
The first append works because the browser knows what to do with the <svg> element, and does the right thing. However the second append fails because the <mask> element will be created in the default (ie. HTML) namespace rather than the SVG one.
If you look at the DOM properties of the appended <mask> element in your browser's dev tools, you will probably find that it has the wrong (ie. not SVG) namespace.
As a solution, I would try adding the <mask> back in to the original SVG and only set the mask attribute when you want the mask to be used. In other words, remove:
mask="url(#mask1)"
then when you want the mask to be applied:
document.getElementById("id-of-masked-circle").setAttribute("mask", "url(#mask1)");

Convert svg-element to image

i have a html svg element (not canvas) and have to save the content to a image file (png or jpg).
Is there any solution for this?
canvas.toDataURL() didn't work, because it's a svg element.
example:
<svg:svg id ="svg">
<svg:svg width="{width}" height="{height}">
<svg:circle cx="{cx}" cy="{cy}" r="{radius}" id="circ" fill="white" stroke="black" stroke-width="2" />
<svg:text x="{tposW}" y="30" line="0" text-anchor="middle">{VORNAME}</svg:text>
<svg:text x="{tposW}" y="44" line="1" text-anchor="middle">{NAME}</svg:text>
<svg:text x="{tposW}" y="58" line="2" text-anchor="middle">{GEB}</svg:text>
</svg:svg>
</svg:svg>
Please post only solutions without using jQuery.
Use the canvg JavaScript library to render the SVG image using Canvas:
http://code.google.com/p/canvg/
and then use the canvas.toDataURL()

Categories

Resources