I am trying to dynamically create SVG vectors with Javascript and I'm running into some problems.
I create the svg markup in javascript with document.createElement and append it to a div container. Then I do the same thing and create a rect and add it as a child to the svg id.
When I look at the DOM, I can see the elements being added, and their properties are such:
<svg id="gdc_container" xmlns="http://www.w3.org/2000/svg" version="1.1" width="500" height="300">
<rect id="gdc_background" width="500" height="300" style="rgb(0,0,255)"></rect>
</svg>
Everything looks fine, however the graphics are not showing up. When I use Chromes inspector, I can see the svg and rect elements, but the chrome tooltip shows their size as [ 0 x 0 ].
Anyone have any ideas?
Did you try using createElementNS? (developer.mozilla.org/en/DOM/document.createElementNS)
For some examples look at http://www.kevlindev.com/tutorials/basics/shapes/js_dom/index.htm.
Related
So I have an <g> tag in an svg element with a clip-path that consists of a rectangle defined by {x:0,y:0,width:1000,height;800}. I added the clipping path as I wanted to hide some overflown children of this tag.
When I select this tag and call either getBBox() or getBoundingClientRect() for some reason I get the rectangle of the clipping path - not the dimensions of the overflow.
This is strange for two reasons:
mouse-over the element in the browser inspector (mozilla and chrome) shows the correct dimensions (width:1200, height:800).
in a similarly structured document, these methods return the dimensions with overflow.
So what is the correct behavior? and how do I get the full width of an svg element with clip-path hidden elements?
Both the SVG 1.1 spec and the CSS masking spec state this:
A clipping path affects the rendering of an element. It does not affect the element’s inherent geometry. The geometry of a clipped element (i.e. an element which references a <clipPath> element via a clip-path property, or a child of the referencing element) must remain the same as if it were not clipped.r
And this is what happens in the example below. So this might not be what your result is about.
Note that the results for .getBBox() and .getBoundingClientRect() differ. That is because the first states size in the local userpace coordinate system, while the latter states size in screen pixels. It might not be obvious that a transformation has been taking place between the two, as it might be hidden implicitely in the relation between viewBox, width and height attributes of the <svg> element.
const clipped = document.querySelector('#clipped');
const bbox = clipped.getBBox();
console.log(bbox.x, bbox.y, bbox.width, bbox.height);
const bcrect = clipped.getBoundingClientRect();
console.log(bcrect.x, bcrect.y, bcrect.width, bcrect.height);
<svg width="400" height="300" viewBox="0 0 200 200">
<clipPath id="cp">
<rect x="50" y="50" width="100" height="100" />
</clipPath>
<rect id="clipped" width="200" height="200" clip-path="url(#cp)" />
</svg>
I'm working with animated SVGs / Snap.svg for the first time, so please forgive my lack of knowledge on this subject.
I made a series of 3 animated SVG "frames" (400x300px), each nested within a larger SVG (1200x300px) to contain them all. A div element with a clip style property hides the other two "frames" when they're not ready to be shown.
Using Snap.svg, each frame is supposed to "slide" into view using translate after a certain amount of time, and within each frame are some animated elements.
Long story short: the animation looks perfect in Firefox, but it looks awful in Chrome/Webkit. In Chrome, it looks like each of the frames are just being stacked on top of each other instead of side-by-side.
In addition, two of the elements (the cow circle joules and the graph circle graph) are rendering in the upper-left corner instead of using their translate property to position them in the center-right area.
You can see the animation in Plunker. Please try it out in both browsers to see what I mean.
http://plnkr.co/UhTy83
Firefox GIF screen capture:
Chrome GIF screen capture:
Thanks Ian in the comments to my question! Swapping out the <svg> tags for <g> (group) tags fixed this problem. It's interesting to me that Firefox allows you to transform <svg> elements but Webkit does not.
Before:
<svg class="slides" width="1200" height="300">
<svg class="slide1" width="400" height="300"></svg>
<svg class="slide2" width="400" height="300"></svg>
<svg class="slide3" width="400" height="300"></svg>
</svg>
After:
<svg class="slides" width="1200" height="300">
<g class="slide1"></g>
<g class="slide2"></g>
<g class="slide3"></g>
</svg>
I have the following code:
<div class="blah" style="clip-path: url(#clippath)"></div>
<svg width="0" height="0"><defs><clipPath id="clippath">
<rect x="0" y="0" height="100" width="100"></rect>
</clipPath></defs></svg>
This correctly clips the blah div to 100x100 square. However, if I use JavaScript to add the svg to the DOM (rather than it being there from page load), it no longer works. Specifically, I'd like to create a dynamic clip path based on events happening in my app.
What am I doing wrong? This only needs to work in Firefox (which unfortunately doesn't support clip-path: polygon(...))
Make sure you are you using the namespace variant of createElement.
document.createElementNS("http://www.w3.org/2000/svg", "clipPath");
If you are just using createElement() then the element will be going into the default namespace (ie HTML) and won't be recognised by the SVG renderer.
I have a blank svg canvas on my page
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
width="100%"
height="100%"
id="svg_canvas"
preserveAspectRatio="xMinYMin meet"
viewBox="0 0 250 250">
</svg>
I have another svg, with the following form, in an external file.
<?xml ...>
<svg id="external"
...>
<g id="lollipop"
...>
//LOTS OF VECTOR INSTRUCTIONS IN THIS GROUP
</g>
</svg>
I would like to ajax for the external svg and insert the lollipop group into the current canvas. I would accept answers which ajax for the entire .svg and parse out the group (id="lollipop") to insert. It may be preferable, though, to copy just the group i want and save that as a text file which can be called upon and parsed. Either way.
Using jQuery, here is the basic idea of what you want:
//here I have a hidden div element in my document called "hiddenDiv";
//I'm loading the SVG into the DOM via AJAX so we can access its elements.
$("#hiddenDiv").load("/some/path/to/yourfile.svg", function(){
//grab the content you want and add it to the canvas.
$("#lollipop").clone().appendTo("#svg_canvas");
//get rid of the extra unneeded svg
$("#hiddenDiv").empty();
}
EDIT After reading up on load, it has a nifty feature to easily do what you want:
$("#svg_canvas").load('/some/path/to/yourfile.svg #lollipop');
link to the docs: http://api.jquery.com/load/ see "Loading Page Fragments".
I'm trying to create (what I thought would be!) a simple re-usable bit of SVG to show three lines of text, with a background colour - to simulate a 'post-it' note.
I have found some useful code here to get the Bounds of the Text http://my.opera.com/MacDev_ed/blog/2009/01/21/getting-boundingbox-of-svg-elements which I am using.
So: I'm creating an group of text elements like this in the 'defs' section of my SVG:
<svg id="canvas" width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="post_it">
<text x="0" y="30" id="heading" class="heading">My Heading</text>
<text x="0" y="45" id="description" class="description">This will contain the description</text>
<text x="0" y="60" id="company" class="company">Very Big Company Ltd.</text>
</g>
And I'm displaying the text with a 'use' element like this:
<use id="12345" class="postit" xlink:href="#post_it" onclick="showId(this);"/>
I'm using the onclick to trigger a call to the following javascript function (defined in 'defs' section):
function showId(elem) {
post_it_rect=getBBoxAsRectElement(elem);
document.getElementById('canvas').appendChild(post_it_rect);
}
(The 'getBBoxAsRectElement(elem)' is from the link I posted).
As this stands; this works just fine - however if I change my 'use' element to position the text in a different place like this:
<use x="100" y="100" id="12345" class="postit" xlink:href="#post_it" onclick="showId(this);"/>
Now, the text displays in the correct place, but the resultant 'background-color' (actually a 'rect' element with opacity of 0.5) still shows on the top-left of the svg canvass - and the function used to calculate the rect is returning '-2' rather than '100' ('-98'?) as I need (I think).
What do I need to do to line up the 'rect' elements and the text elements ?
The author of the (very helpful article btw) script provides a more advanced script to draw a box round any 'bb' in an SVG, but I couldn't get this to work (missing 'transform' functions?).
I'm using Firefox 7.x to render the SVG ; and I'm loading a .svg file (ie, not embedded in html etc) straight from disk to test this).
Yes, you may need to compensate yourself for the x and y attributes on the <use> element for the time being, I'll try to find some time to update the blogpost and script.
Here's a draft SVG 1.1 test that among other things checks that the effect of the x and y attributes are included in the bbox. The line starting [myUse] is the one that tests this case, if it's red then that subtest failed. Chromium and Opera Next both pass that subtest, while Firefox nightly and IE9 doesn't. Note that the test itself has not gone through full review yet, and that it may still change.