Animate SVG path from <use> tag - javascript

I'm trying to animate an svg path using this technique by Jake Archibald
The technique works well when the svg code is pasted in my document, but fails when I'm referencing the svg from my "defs.svg" file.
My defs.svg looks like this:
<svg xmlns="http://www.w3.org/2000/svg">
<symbol viewBox="0 0 48 50.6" id="icon-result" fill="#FFF">
<path d="M19 49.4c-2 1.7-5.1 1.5-6.9-.5l-11-12.8c-1.7-2-1.5-5.1.5-6.9 2-1.7 5.1-1.5 6.9.5l11 12.8c1.8 2.1 1.6 5.2-.5 6.9z"/>
<path d="M13.6 49.4c-2.2-1.5-2.9-4.5-1.4-6.8L39 2.2C40.5 0 43.5-.7 45.8.8c2.2 1.5 2.9 4.5 1.4 6.8L20.3 48.1c-1.4 2.2-4.5 2.8-6.7 1.3z"/>
</symbol>
</svg>
In my code I do the following:
<svg class="Dashboard__nav-icon">
<use xlink:href="/svg/defs.svg#icon-result" />
</svg>
When trying to animate the code this way I get null when trying to get the path by using Jake Archibalds technique linked above. Is there a way to get the path while still using a separate defs.svg file and the <use> tag?

You won't be able to get the DOM path element via the second <svg> element. That is because elements referenced via a <use> do not appear in the DOM tree where they are used. So the following will not work:
var path = document.querySelector('.Dashboard__nav-icon path');
The symbols elements are not visible in the main document's DOM tree.
However you would be able to reference the paths directly via their definition. But for that to work you would need to inline the defs.svg file.
So, if the defs.svg is in the same file, then you would be able to use:
var path = document.querySelector('#icon-result path');

Related

How to use SVG with React and generate icons

Is there any place where I can upload an icon and get an SVG string vector?
Also, what are those tags in the code below?<g> <path> and viewBox, data-original, xmlns, d tags are?
Lately, Is the performance worth using SVG in place of regular icons?
<svg viewBox="0 0 512 512">
<g xmlns="http://www.w3.org/2000/svg" fill="currentColor">
<path d="M0 0h128v128H0zm0 0M192 0h128v128H192zm0 0M384 0h128v128H384zm0 0M0 192h128v128H0zm0 0"
data-original="#bfc9d1"
/>
</g>
</svg>
Here's a very good guide to using SVG with react. https://www.sanity.io/guides/import-svg-files-in-react
There are many online convertors you can use to create svg's
e.g https://www.pngtosvg.com/
The SVG <g> element is used to group SVG shapes together.
The SVG <path> element indicates that the vector to draw is a path. The could alternatively be a polyline or a shape e.g circle.
The <viewBox> attribute is a list of four values: min-x, min-y, width and height
The xmlns attribute is XML Namespace which is needed to use the correct DTD - Doctype Declaration
The <d> attribute defines the path that will be drawn.
From my experience SVG performs significantly faster when using inline SVG's.
The main blocking element for page loading is the numberous amount of files that load sequentially. Using inline svg loads all the images within the page file.
The major benefit of SVG's are the scalability of vector over raster when zooming or viewing at differant resolutions.

How can you dynamically import, modify, and export vector files?

I want to be able to import SVG files from the file system, layer them, change their fill colors, and then export them as a single vector file.
The solution to this problem was simply to import the SVG data as a string, swap out constants for various properties, and then write the new string to the file system.
For example, the following svg displays an orange circle:
<svg viewBox="0 0 1000 1000" xmlns="http://www.w3.org/2000/svg">
<circle cx="500" cy="500" r="500" fill="orange"/>
</svg>
If you wanted to be able to dynamically change the color of the circle, you could just replace the "orange" text with a placeholder name, such as "{FILL_COLOR}". Now, the SVG should look like this:
<svg viewBox="0 0 1000 1000" xmlns="http://www.w3.org/2000/svg">
<circle cx="500" cy="500" r="500" fill="{FILL_COLOR}"/>
</svg>
In your code, you can load the file from the file system as a string and then make changes to it as needed; you could then write the result to the file system. This solution also works perfectly on frontends. Instead of loading the SVG from the file system, simply have it as a hard-coded constant. Once you make the changes to the SVG, you can use the result however you want.
function getCircle(color) {
const svg = `<svg viewBox="0 0 1000 1000" xmlns="http://www.w3.org/2000/svg"><circle cx="500" cy="500" r="500" fill="{FILL_COLOR}"/></svg>`;
return svg.replace('{FILL_COLOR}', color);
}
The best source to refer would be their official docs: https://svgjs.dev/docs/3.0/getting-started/
The next in the line source would be stack-overflow's search by svg.js tag: https://stackoverflow.com/questions/tagged/svg.js
Now a quick intro from my side.
SVG.JS lets you work very extensively with SVGs. You can import an SVG into your code and start manipulating it. You can change color, size and even add events and animations.
However before you start working with SVGs, you should make sure that the SVG is compressed using some online tool like this: https://jakearchibald.github.io/svgomg/ and after compressions you should edit your SVG file by adding class selectors within your SVG for paths (regions) so that you can select these paths from your JS code. Example of SVG from one of the projects where I used this technique (trimmed for the sake of simplicity):
<svg id="svg-map" class="svg-map" version="1" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 363 624">
<path class="svg-path-1" fill="#FFFFFF" stroke="#FFFFFF" stroke-miterlimit="10" d="M114 ... 24 42z"/>
<path class="svg-path-2" fill="#FFFFFF" stroke="#FFFFFF" stroke-miterlimit="10" d="M114 ... 24 42z"/>
<path class="svg-path-3" fill="#FFFFFF" stroke="#FFFFFF" stroke-miterlimit="10" d="M114 ... 24 42z"/>
</svg>
You can save your SVG in some folder and render it using the image tag on your HTML page.
Install in your project (Example using NodeJS):
npm install svg.js
Then import this library into your JS code like this:
import { SVG } from '#svgdotjs/svg.js'
And now you can select the SVG from your page using the code like this:
let mapContainer = SVG.select('.some-svg-class');
let myMapObject = mapContainer.first();
And set the viewport:
myMapObject.viewbox(0, 0, 500, 700);
Now you can loop across all the paths (regions) of the map and perform some manipulation like this:
let svgRegionPaths = myMapObject.select('path');
svgRegionPaths.each(function(i, children) {
this.opacity(0.7);
});
This is just an example. You can do a lot more with this library. I haven't shown the example of selecting paths by class selectors but I hope you get the idea.
I was able to pair this with React and make it work more in line with my coding style and requirements for the application.

Draw curved lines with css or canvas

I need to draw dynamic curved lines to show flight durations.
Any idea how I can do it?
I was try with clean css but have some rendering problems, I think only method is to use canvas.
You could use SVG it's a bit more browser agnostic I suppose.
SVG Browser Support
Canvas Browser Support
Something like this in your HTML :
<?xml version="1.0" standalone="no"?>
<svg width="190px" height="160px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<path d="M10 80 Q 95 10 180 80" stroke="black" fill="transparent"/>
</svg>
Of course this could be generated via Javascript and then rendered. JSFiddle
SVG Tutorial
A Brief intro into the dynamic JS generation would be something along these lines. :
Create your dom element :
<svg id="flight" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
</svg>
Now we add some JS attributes that you will generated based on variables in flight info:
var svgNS = "http://www.w3.org/2000/svg";
var flightPath = document.createElementNS(svgNS,"path");
flightPath.setAttributeNS(null,"id","path_1");
//This is what you need to generate based on your variables
flightPath.setAttributeNS(null,"d","M10 80 Q 95 10 180 80");
//Now we add our flight path to the view.
document.getElementById("flight").appendChild(flightPath);
Add some CSS Animation to make it a little prettier and you end up with the following example :
JSFiddle Dynamic

SVG childNodes return text

I have an inline SVG and try to loop over all paths within a group element. When using childNodes I found out that Browsers add an extra text child for every path. I am curious why browsers are doing so and if there is a smart way to just loop over real child elements.
I've created a little JSBin to demonstrate the behaviour: http://jsbin.com/tutisakege/1/edit?html,css,js,console,output
(Check the output of the console)
HTML
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 100 100">
<g id="test">
<path d="M50 0 L0 100 L100 100 Z" />
<path class="red" d="M25 0 L25 25 L75 0 L75 25 Z" />
</g>
</svg>
JS
var group = document.querySelector('#test'),
children = group.childNodes;
The childrenobject now holds 5 entries even though my test group only has 2 paths.
Note: I know I could loop over all entries and check whether I have an instance of SVGPathElement but that seems a but cumbersome to me.
(I've tested it in the latest Chrome and Firefox)
Browsers didn't add it, they are right there in the document source. There is whitespace between the path elements i.e. carriage returns and spaces.
You can skip the text by using element.children but that apparently only works on Firefox and Chrome so if you want it done portably you'll probably need to stick with checking for element instances as you suggest in the question.

Can't read SVG element contents in mobile Safari with Javascript

To reuse certain SVG objects I have symbols defined in an SVG element which is written out at the top of my DOM. To display an SVG I can do:
<svg><use xlink:href="#symbol-identifier" /></svg>
To animate some SVG's I use Snap.svg, but I can't seem to create a Snap object from an SVG referenced by an xlink. To work around this I want to inject the SVG contents (already present in the DOM) at runtime into the SVG currently using the xlink.
To do this I need to get the contents (innerHTML?) of a symbol. This works in every browser i tested, except for mobile safari.
Below a simple test setup (without the symbol-wrap, but works the same):
HTML:
<div id="outer" style="width: 100px; height: 100px;">
<svg id="inner" viewBox="0 0 100 100" version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle cx="10" cy="10" r="5" fill="#ffffff" />
</svg>
</div>
Javascript:
var outerElement = document.getElementById('outer');
var innerElement = document.getElementById('inner');
outerElement.innerHTML // returns <svg id=".. etc..
innerElement.toString() // returns [object SVGSymbolElement]
innerElement.getAttribute('viewBox') // returns 0 0 100 100
innerElement.innerHTML // returns undefined (in Mobile Safari)
// Everywhere else the content <circle .. etc .. is returned.
Why is this? And is there another way of getting the SVG contents as a string, without regexping away all the unwanted HTML around the outerElement.
XMLSerializer can serialize elements.
var string = XMLSerializer().serializeToString(innerElement);
should work for you.

Categories

Resources