Why does this JS-created SVG <animate> not work in Chrome? - javascript

Consider this simple SVG SMIL animation:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-50 -50 100 100">
<circle r="40" fill="red">
<animate
attributeType="CSS" begin="click"
attributeName="fill" to="blue" dur="0.3s" fill="freeze"/>
</circle>
</svg>
This works correctly in Chrome v18 on Windows (modulo a bug with holding the color):
http://phrogz.net/svg/change-color-on-click-simple.svg
When I generate the <animate> element using JavaScript, all works well in Firefox, Safari, and Opera, but not Chrome. In Chrome, nothing happens when I click on the circle.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-50 -50 100 100">
<circle r="40" fill="red"/>
<script>
var c = document.querySelector('circle');
createOn(c,'animate',{
attributeType:'CSS', begin:'click',
attributeName:'fill', to:'blue',
dur:'0.3s', fill:'freeze'
});
function createOn(el,name,attrs){
var e = el.appendChild(document.createElementNS(el.namespaceURI,name));
for (var name in attrs) e.setAttribute(name,attrs[name]);
return e;
}
</script>
See this JavaScript version here:
http://phrogz.net/svg/change-color-on-click-simple-js.svg
There are no script errors in the console. The content of the first example was actually generated by choosing Copy As HTML from the Chrome Developer Tools after loading the second example, so I know that it is producing the correct attribute names and values. The namespaceURI of the <animate> element is the same between both (the SVG namespace), as is the namespaceURI of all attributes (null).
Is there a way to get JS-generated <animate> elements to work in Chrome?

If I define the attributes before appending the animation, it appears to work.
http://jsfiddle.net/VFUHk/

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.

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.

Cross-platform SVG rotation animation:

Hi I am currently working with some SVG animations that are to work on multiple platforms.
However I have an issue with Internet Explorer (of course).
I use Keith Woods, Jquery SVG extension.
I have mainly been using layers and show, hide to animate my pages but now I need to do a rotation of a line:
$('#topBar').stop().animate({
svgTransform: 'rotate('+angle+','+(545+amplification)+',-260)'}, time);
The SVG object:
<g
id="g3953">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="topBar"
d="m 545,-260 322,0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<rect
y="-260"
x="545"
height="5"
width="5"
id="centerPoint"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
</g>
This partly works but the line called top bar rotates and translates in another manor than on Chrome and Firefox. I.e. it jumps around a little.
Anybody have a solution to this? can i do the translation differently or just use CSS- or other JQuery rotate method to get better support??
I have seen JQuery rotate but does it support translation?
See how the image jumps, below, this occurs when I just moved the point of rotation...
You may fiddle with the code...
IE does not support animations in SVG. The basic way to rotate across all browsers is to use the getBBox() for the container and rotate from the center of the bounding box. As Follows:
<svg id="mySVG" width=400" height="400">
<g id="containerG">
<polygon id="myPolygon" fill="yellow" stroke="blue" stroke-width="1" points="380,80 200,10 40,80 100,320 300,350" />
<circle id="myCircle" cx="170" cy="200" r="40" fill="lime" />
</g>
var deg=30
var bb=containerG.getBBox()
var bbx=bb.x
var bby=bb.y
var bbw=bb.width
var bbh=bb.height
var cx=bbx+.5*bbw
var cy=bby+.5*bbh
containerG.setAttribute("transform","rotate("+deg+" "+cx+" "+cy+")")
From personal experience, animations are not meaningfull to me in most svg applications. However, what is important, are transitions. By transitions, I mean the smooth movement of a graphic element's attributes as they visually change. I think currently the best cross-browser handling of svg transitions is by D3. The D3 api may be more than you want to get into at this time...But if you plan to work seriously with svg, you should give it a try.

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...

Categories

Resources