How do you animate an SVG path using JavaScript - javascript

is there a way that I can animate the "d" attribute of the following svg path?
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<path id="theEl" d="M0 0, L 0 500, L 600 500, L 600 0" stroke="black" stroke-width="10"/>
</svg>
If I want to change it I know that this would be enough:
document.getElementById('theEl').setAttribute( 'd', 'M0 0, L 0 200, L 200 200, L 600 0' );
But what if we want this to animate/morph? Say I have a button that on click it toggles between these 2 paths.
I've seen a couple of answers about it and they suggest to create an SVG animation element and then add it to the svg DOM element. But this would mean that every time the button is clicked we would need to append/remove that animation element right? Isn't there a simpler way? Without using any svg libs?
Thanks a bunch!

Just create the SMIL animation statically i.e. as markup as a child of the path and have it run when the button is clicked.
<animate begin="click" ...

You can add the animation elements to the svg directly, no need for scripting.
For a neat example and some further details, see this blogpost by Tavmjong Bah.

Related

Why is SVG textPath not working in Firefox when rendered by React

I'm trying to render a react component with an inline SVG element that has a text along a path. This is what is returned from the render method:
<div className="textsvg">
<svg width="100%" height="100%" viewBox="-50 -50 100 100">
<defs>
<path id="textPathTop" d={`
M 0 40
A 40,40 0 0 1 0,-40
A 40,40 0 0 1 0,40`}></path>
<path id="textPathBottom" d={`
M 0 -41.8
A 41.8,41.8 0 0 0 0,41.8
A 41.8,41.8 0 0 0 0,-41.8`}></path>
</defs>
<use xlinkHref="#textPathBottom" fill="none" stroke="red"></use>
<text fill="red" fontSize="4.5"><textPath xlinkHref="#textPathBottom">We go up, then we go down, then up again</textPath></text>
</svg>
</div>
This shows the "We go up, then we go down, then up again" text, but just in a straight horizontal line starting from 0,0.
Copying the resulting html into a codepen shows the result as it should look, using the textPath.
Why is the textPath ignored when rendered with ReactJS?
Using React 15.3.1 and checking in FF 52.0.2(32bit)
Already tried using _dangerouslySetInnerHTML for textPath, but that didn't work either.
Check if you have a <base href="..."> tag in your <head> element.
If so, Firefox won't be able to display your text, while Chrome will.
Firefox is searching for your xlink:href attribute at the base href url, it does not find it, so the text is just ignored.
A workaround is to use an absolute url :
<textPath xlink:href="http://pathtoyourpage#textPathBottom">
It is easier if you generate the svg with javascript :
textPath.setAttribute('xlink:href', `${location.href}#textPathBottom`)
Some similar weird behavior happen with mask and filter attributes.

Movement in html5 canvas

I am trying to figure out the right way to do movement in canvas. From my research it appears that a lot of tutorials use redrawing. I got movement to work by clearing my shapes and recreating them (redrawing?): http://pastebin.com/VuYdtM2U
I am wondering though if that is the way it is supposed to be done or if there is a better way. I can imagine clearing an image and creating it a pixel over every fraction of a second would get resource intensive.
Generally, It is done this way only. Most of the Browser Games are coded this way and a lot of clearing and re rendering takes place seamlessly. Modern Browsers can handle a lot of rendering without any delay.
The main reason behind this is that, Canvas is just a sort of image for the browser and it just draws on it and don't have to retain any Element DOM Structure.
Whereas in case of SVG, all elements have to be appended / deleted /added to the DOM Structure which takes toll if there are a lot of elements.
There are several factors which can help us decide which technique is better as per the case.
Generally for Page with DOM elements less than 10,000... Both are efficient.
You can also use mixture of Canvas , SVG and Multiple Canvas (like you may want to show a hover canvas, over another canvas)..
Try this
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" height=400 width=500>
<g>
<rect transform="translate(-20,-20)" height="40" width="40" style="fill:#777; stroke:none;"/>
<animateMotion fill="freeze" path="M 0 0 Q 190 160 150 70 T 200 150 T 300 200 T 200 200" dur="3.14159s" repeatCount="indefinite"/>
</g>
<path d="M 0 0 Q 190 160 150 70 T 200 150 T 300 200 T 200 200" style="fill:none;stroke:#F00;stroke-width:5"/>
</svg>

For some reason the SVG does not update and render the markers

I am making a SVG with snapsvg and generating a path with
paper.path("M" + e.pageX + " " + e.pageY + "L20 20");
I have a hover event and the class "selected" gets added to the path
.selected class references this marker
<defs>
<marker id="knob" viewbox="0 0 10 10" refx="0" refy="5" markerunits="strokeWidth" markerwidth="4" markerheight="3" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z"></path>
</marker>
</defs>
I am linking the markers to the class like this
<style>
.selected{
stroke: #006600;
marker-mid:url(#knob);
marker-end:url(#knob);
marker-start:url(#knob);
fill: #00cc00;
}
</style>
For some reason the SVG does not update and render the markers. However when I copy the SVG from the the page and paste it back in the page using chrome developer tools it updates and shows the markers.
Does any one know why it would be doing this?
I have made a JSFiddle of with a snippet of random crap I was making http://jsfiddle.net/XTD4x/
If you click in the Result window it will start drawing a line. Click to add nodes. double click to stop drawing. hover to add the class selected
The problem is you put the <marker> element (and, strictly speaking, the <style> element as well) in the wrong namespace. It needs to go into the SVG namespace, but jQuery's functions like .prepend() put it into the HTML namespace (as they rely on .innerHTML, I guess). Therefore, use either DOM methods like .createElementNS(), or use DOMParser, like
$("svg defs").prepend(
(new DOMParser()).parseFromString(
'<svg xmlns="http://www.w3.org/2000/svg"><style type="text/css"> .selected{ stroke: #006600; marker-mid:url(#knob); marker-end:url(#knob); marker-start:url(#knob); fill: #00cc00; }</style><marker id="knob" viewBox="0 0 10 10" refX="0" refY="5" markerUnits="strokeWidth" markerWidth="4" markerHeight="3" orient="auto"><circle cx="6" cy="6" r="5" /></marker></svg>',
"image/svg+xml"
).documentElement.childNodes
);
Notice I wrapped the elements into a complete SVG document so that I can parse it.
Try the modified fiddle.
Second answer: Define the marker outside the SVG generated by snapsvg and put the CSS in an external stylesheet or another <style> element outside the snapsvg SVG. No JavaScript required for this, no need to parse and insert anything.
See another fiddle.

mousemove target element returns not required or correct element

I have a set of svg element
<svg id="container_svg" style="width: 700px; height: 600px;>
<rect width='600' height='600'> </rect>
<g>
<path d="M 285 0 L 285 0 L 318.34499999999997 57.5055 L 251.655 57.5055 z"/>
</g>
</svg>
and added a mousemove event to svg element in document.ready as
$("#container_svg").mousemove(function(evt){
var child=$(evt.target)[0].nodeName;
});
So moving on svg element, the event get triggered but getting different target element in different browser as below.
Even though moving on the path element in svg, i am getting $(evt.target)[0].nodeName as "rect" in firefox,
but in IE and chrome $(evt.target)[0].nodeName returns "path" as i want...
Thanks,
Siva
Cross browsers solution:
$("#container_svg *").mousemove(function(evt){
//var child=$(evt.target)[0].nodeName;
evt.stopPropagation();
var child=$(evt.currentTarget)[0].nodeName;
});
don't know why you down-voted my previews answer...

Getting boundingClientRect for a clipped element

I need to get a boundingClientRect of an SVGElement which has been clipped - using this information, i want to position HTML elements on top of the SVG element.
My SVG looks like this (simplified, the original one has both multiple cascading viewports and multiple cascading transforms):
<svg width="96" height="80">
<clippath id="clip">
<path d="M 0 0 L 96 0 L 96 80 L 0 80 Z"/>
</clippath>
<g clip-path="url(#clip)">
<image x="0" y="-8" width="96" height="96" xlink:href="…" transform="…"/>
</g>
</svg>
I want to position HTML elements on top of the SVGGElement. To do this, i get the boundingClientRect of
the HTMLElement being positioned
the HTMLElements parent
Element ? that allows me to get the boundingClientRect of the SVGGelement with Clipping applied
In Chrome, this works by getting the boundingClientRect of the SVGPathElement. In Opera and Firefox, the resulting clientRect has width and height 0 (probably because the path element isn't displayed). The boundingClientRect of the SVGGelement itself gets its dimensions from contained SVGImageElement regardless of the clip-path applied.
I tried referencing the SVGPathElement through a SVGUseElement and getting the boundingClientRect of that - which seemed to work.
Is it really necessary to introduce another DOM element that is set to visibility hidden and pointer-events: none or does anyone know a better solution?

Categories

Resources