I'm trying to use JavaScript to create an SVG rectangle with text in it. I've already got the rectangle (generated by morris.js, a charting library) but can't seem to be able to get the text in.
The output I want to achieve would look a bit like this:
<svg>
<g>
<rect x="0" y="0" width="100" height="100" fill="red"></rect>
<text x="0" y="50" font-family="Verdana" font-size="35" fill="blue">Hello</text>
</g>
</svg>
This is my code now (snippet will not run because rectangleElement is undefined here):
console.log(rectangleElement);
/* output:
<rect x="1205.5019308035712" y="0" width="193.5206919642857" height="307" r="0" rx="0" ry="0" fill="#E5E5E5" stroke="none" fill-opacity="0.8" style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0); fill-opacity: 0;"></rect>
*/
var t = document.createElementNS('http://www.w3.org/2000/svg', 'text');
var g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
g.appendChild(t);
g.appendChild(rectangleElement);
console.log(g);
/* output:
<g><text></text></g>
*/
As you can see, the rectangleElement isn't being added to my group (g). I'm also not getting any errors in the console when trying to append the rectangle to the group.
Any ideas on what I could be doing wrong?
I ended up fixing it myself. I'm not at all sure what the issue was, when I added the seemingly unrelated code below, it started working.
var txtElem = document.createElementNS("http://www.w3.org/2000/svg", "text");
txtElem.setAttributeNS(null,"x",20);
txtElem.setAttributeNS(null,"y",40);
var theText = "text";
var theMSG = document.createTextNode(theText);
txtElem.appendChild(theMSG);
Related
I need to add SVG objects to specific locations inside an SVG object that's appended to the DOM.
But whenever I do that I see nothing rendered on the screen. I can see the SVG objects are added (in Elements tab of DevTools) but they're not rendered. They're pure SVG (not wrapped around an HTML element like a DIV).
I've tried loading SVGs with ajax and adding them, tried to do with Snap, tried to have these elements inside a <defs> tag, find them with Snap and then add them to the main Snap object. Nothing seems to work. The objects are always added but not rendered.
Is that even possible?
The SVG
<svg width="400" height="300" style="background: gray">
<defs>
<circle id="redc" cx="50" cy="50" r="50" style="fill: red" />
<circle id="yelc" cx="40" cy="40" r="40" style="fill: yellow" />
</defs>
<circle id="bluc" cx="200" cy="200" r="50" style="fill: blue" />
</svg>
JavaScript
const s = Snap("#root");
Snap.load('images/all.svg', function(data){
var all = data;
// append the all.svg node. cool
s.append( all.node );
// get the red circle definition
var redc = all.select('#redc');
s.append(redc.node); // doesn't work
});
with foreign object:
Snap.load('images/all.svg', function(data){
var all = data;
// append the all.svg node. cool
s.append( all.node );
// get the red circle definition
var redc = all.select('#redc');
// foreign object
var foreign = document.createElementNS('http://www.w3.org/2000/svg',"foreignObject");
foreign.setAttribute('width', 500);
foreign.setAttribute('height', 150);
foreign.appendChild(redc);
// add the foreign object - doesn't work
s.append( foreign );
});
It doesn't work because you're appending the circle outside of the <svg> tree i.e. directly under #root which is probably some kind of HTML element such as a <div>
The foreignObject problem is basically the same. Not sure why you're trying to add a circle as a child of a foreignObject (that won't work as you'd need an svg element to be its parent). I've used an html element instead.
const s = Snap("#root");
var svg = '<svg width="400" height="300" style="background: gray"><defs><circle id="redc" cx="50" cy="50" r="50" style="fill: red" /><circle id="yelc" cx="40" cy="40" r="40" style="fill: yellow" /></defs><circle id="bluc" cx="200" cy="200" r="50" style="fill: blue" /></svg>';
var all = Snap.parse(svg);
// append the all.svg node. cool
s.append( all.node );
// get the red circle definition
var redc = all.select('#redc');
all.node.append(redc.node); // append as a child of the svg node
// foreign object
var foreign = document.createElementNS('http://www.w3.org/2000/svg',"foreignObject");
foreign.setAttribute('width', 500);
foreign.setAttribute('height', 150);
foreign.setAttribute('fill', 'pink');
var p = document.createElement('p');
foreign.appendChild(p);
var text = document.createTextNode("Hello World");
p.appendChild(text);
// add the foreign object to the correct part of the tree
all.node.append( foreign );
<script src="http://snapsvg.io/assets/js/snap.svg-min.js"></script>
<div id="root"></div>
I know a very easy way to get the current matrix transformation of any SVG element:
// 't' is a string
var t = window.getComputedStyle(nativeElement, null).transform
console.log(t);
The problem is that the previous method returns numbers with no more than six decimals. For example, the previous code may return:
matrix(0.965926, 0.258819, -0.258819, 0.965926, 0, 0)
Is there a way to get the matrix transformation of any SVG element more accurately?
To get the current transform attribute as an SVGMatrix object, you can use:
element.transform.baseVal.consolidate().matrix
var myrect = document.getElementById("myrect");
console.log(myrect.transform.baseVal.consolidate().matrix);
<svg>
<rect id="myrect" width="10" height="10" transform="scale(2) rotate(45)"/>
</svg>
Consolidation can change your element 'transform' attribute value.
You can also get a matrix without changing the transformation attribute by transforming the element matrix to the parent.
See documentation about the:
getTransformToElement
function getMatrix(element) {
var matrix = element.parentNode
.getScreenCTM()
.inverse()
.multiply(element.getScreenCTM());
return matrix;
}
var myrect = document.getElementById("myrect");
console.log(getMatrix(myrect));
<svg>
<rect id="myrect" width="10" height="10" transform="scale(2) rotate(45)"/>
</svg>
In the case if you know that your SVG element has no ancestors wich were transformed you can use SVGelement.getCTM() function for it because it is shorter. I think that CTM in the function name is the short form from «current transformation matrix».
var rect = document.querySelector("#rect");
console.log(rect.getCTM());
<svg>
<rect id="rect" width="10" height="10" transform="scale(2) rotate(45)"/>
</svg>
Difference rect.getCTM() vs. rect.transform.baseVal.consolidate().matrix
But you should be careful about the use from this function because it only gives the same result like from the matrix rect.transform.baseVal.consolidate().matrixas long as no ancestor elements have a transform. For example:
var rect = document.querySelector("#rect"),
ctmMatrix = rect.getCTM(),
baseValMatrix = rect.transform.baseVal.consolidate().matrix;
console.log('CTM Matrix: translateX = '+ctmMatrix.e+', translateY = '+ctmMatrix.f);
console.log('BaseVal Matrix: translateX = '+baseValMatrix.e+', translateY = '+baseValMatrix.f);
<svg>
<g transform="translate(35,45)">
<rect id="rect" width="10" height="10" transform="translate(35,45)"/>
</g>
</svg>
I thank #PaulLeBeau for the explanation about the difference between this matrixes.
In jQuery you can simply creating an element by providing a string, like so:
var newElement = $('<div></div>');
How can I do the same thing in Snap SVG? I have a string like
<rect x="100" y="100" transform="matrix(-0.7071 0.7071 -0.7071 -0.7071 1825.4047 1024.3746)" fill="#7EC242" width="230.02" height="56.723" stroke="#64bc46"/>
And I need an Element I can append to an already existing Snap document. I tried fragments, but they don't have all functions I need to manipulate the element afterwards.
I believe you can do it with parse (I removed the transform so the code could work in the small SO snippet window):
var s = Snap("#svg");
var string = '<rect x="100" y="100" fill="#7EC242" width="230.02" height="56.723" stroke="#64bc46"/>'
var rect = Snap.parse(string);
s.append(rect);
<script src="http://cdn.jsdelivr.net/snap.svg/0.1.0/snap.svg-min.js"></script>
<svg id="svg" width="400" height="200">
</svg>
I want to scale the element created by use by keeping the fixed position in svg.
I read this
but my element is created by use
so it shows
Simply I can remove the old one and create a new one ,but I feel this a bit trouble.
So I wonder whether it exists any convenient way?
function tableBindMouseClick(parametersObject)
{
var table = document.getElementById("PointsTable");
var length = $('#PointsTable tbody tr').length;//get table rows number
for(var i =0;i<length;i++)
{
var id = i;
$($('#PointsTable tbody tr')[i]).bind('click',
(function(id)
{
return function()
{
var p = parametersObject.pointArray[id];
var x = p[0] -5;//coordinate x
var y = p[1] -5;//coordinate y
var icon = document.getElementById("point"+id);
icon.setAttributeNS(null, "transform", "translate("+-x+"," + -y +") scale(3) translate("+x+","+y+")");
};
})(id));
}
I do not know whether it is enough.
I am still modifying it.It can run but its effect is still incorrect.
The result
I can not see the error...
PS:Unfortunately,I use the defs element instead of symbol element to create icon.I also want to know difference in them,including g element.
I complete it by this parameters:
icon.setAttributeNS(null, "transform", "translate("+-2*x+"," + -2*y +")" + " scale(3)" );
I think the post makes some mistakes...
This example may make sense.
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>Scalable Vector Graphics (SVG) 1.1 (Second Edition)</title>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1" width="5000" height="5000" viewBox="0 0 5000 5000">
<rect x="100" y="100" width="30" height="30" stroke="black" fill="transparent" stroke-width="5"/>
<rect x="100" y="100" width="30" height="30" stroke="black" fill="transparent" stroke-width="5"
transform ="translate(-230,-230 ) scale(3)" />
</svg>
</body>
</html>
<svg width="5cm" height="3cm" viewBox="0 0 500 300">
<path id="path1" d="M100,250 C 100,50 400,50 400,250"
fill="none" stroke="blue" stroke-width="7.06" />
<circle r="17.64" fill="red">
<animateMotion dur="6s" repeatCount="1" rotate="auto" >
<mpath xlink:href="#path1"/>
</animateMotion>
</circle>
</svg>
If I write the svg in plain html/svg file, it works fine, the circle animates correctly.
But if I add the circle element dynamically via javascript, circle was added, but it didn't animate. What's wrong? js code:
var svg = $("svg"); //use jquery
var circle = document.createElementNS("http://www.w3.org/2000/svg","circle");
circle.setAttribute("r", "5");
circle.setAttribute("fill", "red");
var ani = document.createElementNS("http://www.w3.org/2000/svg","animateMotion");
ani.setAttribute("dur", "26s");
ani.setAttribute("repeatCount", "indefinite");
ani.setAttribute("rotate", "auto");
var mpath = document.createElementNS("http://www.w3.org/2000/svg","mpath");
mpath.setAttribute("xlink:href", "#path1");
ani.appendChild(mpath);
circle.appendChild(ani);
svg.append(circle);
Use setAttributeNS on "mpath" instead of setAttribute.
mpath.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#path1");
Here's a demo: http://jsfiddle.net/zh553/
Agree with using setAttributeNS on "mpath" instead of setAttribute.
However at least for Chrome (and other WebKit based browsers?) it seems to be necessary to pay attention to http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-ElSetAttrNS where it says the second parameter is the qualifiedName, the qualified name of the attribute to create or alter.
mpath.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", "#path1");
Or, if needed, more generically:
mpath.setAttributeNS("http://www.w3.org/1999/xlink",
mpath.lookupPrefix("http://www.w3.org/1999/xlink") + ":href",
"#path1");
Also discussed at https://bugs.webkit.org/show_bug.cgi?id=22958