How to switch position of D3 SVG elements - javascript

I have some SVG elements on my page that I created with D3. All are children of one parent SVG. Each contains some other D3 elements like paths and text. On the click of a button, I want two of these child SVGs to switch positions, so they move up or down on the page (all are placed above/below each other).
I already tried creating groups ("g") instead of the child SVGs and accessing/changing their positions. However, I can't seem to access the y position of the element.
I also tried using "insertAfter" but this only works with divs, not with SVGs (however, I'm looking for a similar behaviour).
$(".move_up").click(function() {
var svg = $("#second_child"); //ID is actually retrieved from an attribute of the button
svg.insertBefore(svg.prev()); //does obviously not work
});
HTML for move up button (one per child SVG exists):
<a class="move_up">
<span class="grey glyphicon glyphicon-chevron-up" title="Move up"></span>
</a>
HTML for SVG:
<div>
<svg id="parent">
<svg id="first_child"
<path></path>
<rect></rect>
<text></text>
...
</svg>
<svg id="second_child"
<path></path>
<rect></rect>
<text></text>
...
</svg>
<rect></rect>
<text></text>
...
</svg>
</div>
I want the first and second child SVGs to switch positions, when the move up (or respectively a move down) button is used.

This is what I ended up doing:
let group = $("#first_group");
let next_group = $('#second_group");
let diff = 58; // Height of a group
let translate_y = 0;
let translate_y_next = 0;
if (next_group.offset()) {
if (group.attr("transform")) {
let string = group.attr("transform");
translate_y = parseInt(string.substring(string.indexOf("(")+1, string.indexOf(")")).split(",")[1]);
}
if (prev_group.attr("transform")) {
let string_next = prev_group.attr("transform");
translate_y_next = parseInt(string_next.substring(string_next.indexOf("(")+1, string_next.indexOf(")")).split(",")[1]);
}
group.attr("transform", `translate(0, ${translate_y + diff})`);
next_group.attr("transform", `translate(0, ${translate_y_next - diff})`);
}
Works similar for a "Move up" button. Just make sure to change the sign in the last two lines!
May not be super elegant, but does the job.

You are using an SVG as wrapper and the positions are different to html. In SVG You need to define the X and Y position.
let ids = ['ex1', 'ex2', 'ex3', 'ex4', 'ex5']
let btn = document.getElementById('move')
const sortArrayAsYouWish = (array) => {
array.sort(() => Math.random() - 0.5);
}
const changeOrder = () => {
let posY = 35
sortArrayAsYouWish(ids) // change order
ids.forEach((id, i) => {
let $el = document.getElementById(id)
$el.style.transform = `translate(0, ${posY*i}px)`
})
}
btn.onclick = changeOrder
changeOrder()
svg {
width: 500px;
height: 340px;
border: solid 1px #ccc;
}
g {
transition: transform 0.4s;
}
text {
fill: #fff;
text-anchor: middle;
}
#ex2 rect {
fill: blue;
}
#ex3 rect {
fill: yellow;
}
#ex4 rect {
fill: red;
}
#ex5 rect {
fill: cyan;
}
<div><button id="move">Move</button></div>
<svg>
<g id="ex1">
<rect width="120" height="30" x="0" y="0" />
<text x="60" y="15">Hello example 1</text>
</g>
<g id="ex2">
<rect width="120" height="30" x="0" y="0" />
<text x="60" y="15">Hello example 2</text>
</g>
<g id="ex3">
<rect width="120" height="30" x="0" y="0" />
<text x="60" y="15">Hello example 3</text>
</g>
<g id="ex4">
<rect width="120" height="30" x="0" y="0" />
<text x="60" y="15">Hello example 4</text>
</g>
<g id="ex5">
<rect width="120" height="30" x="0" y="0" />
<text x="60" y="15">Hello example 5</text>
</g>
</svg>

Related

Extract Text From SVG [duplicate]

I have this javascript...
window.writeText = function(form) {
var text;
form.catnumber2.value = "PING";
text = document.getElementByName('cat2Number').innerHtml;
return alert(text);
};
But I get no alert box as expected.
The svg does not show up when I view source via view/developer/view source (I'm in chrome) BUT when I use view/developer/developer tools....I can see the following svg....
<svg height="594">
<g ID="MasterG">
<text name="cat2Number">"$1234"</text>
</g>
</svg>
Any idea what I'm doing wrong? Why is it I can't see the svg code in "view source" but I can in "developer tools" ? Is that the cause of my problem? Is that why my alert box won't "alert"?
After a couple of seconds of googling and finding this https://stackoverflow.com/a/9602772/1217408
I created this JSFiddle example of using textContent: http://jsfiddle.net/hVyTJ/1/
The original http://jsfiddle.net/hVyTJ/ uses standard DOM traversal to get to the text element from the root SVG element. While the update targets the text element directly by ID.
As for finding attribute values you can use getAttributeNS as described here: http://www.carto.net/svg/manipulating_svg_with_dom_ecmascript/
EDIT:
As pointed out by Phrogz, a simple getAttribute call is often sufficient. Read the comment for more details.
you can invoke text() to return the text content of an svg:text element.
// assume svgCont is an svg element
var label = svgCont.append("svg:text").text("hello, world!");
// print the text content to the console
console.log( label.text() );
Without all the unnecessary discussion:
Grab your SVG element:
svg = document.getElementById("my_svg_id");
Grab the inner text from the SVG:
var text = svg.textContent
For the ones who need only the displayed text, you can use the Selection API and its Range interface.
Simply using .textContent would also grab all text nodes that aren't displayed:
const svg = document.querySelector("svg");
console.log(svg.textContent);
<svg>
<defs>
<desc>This text is not displayed, it shouldn't be grabbed</desc>
<!-- same for all the new-lines in the markup -->
</defs>
<rect width="100%" height="100%" fill="red" />
<circle cx="150" cy="100" r="80" fill="green" />
<text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text>
</svg>
Since SVG elements don't have an innerText property, we need to iterate ourselves over all text nodes, and check if when selecting them we get a BBox.
const svg = document.querySelector("svg");
const range = new Range();
let textContent = "";
const walker = document.createTreeWalker(svg, NodeFilter.SHOW_TEXT, null);
while(walker.nextNode() && walker.currentNode) {
range.selectNode(walker.currentNode);
if (range.getClientRects().length) {
textContent += walker.currentNode.textContent;
}
}
console.log(textContent);
<svg>
<defs>
<desc>This text is not displayed, it shouldn't be grabbed</desc>
<!-- same for all the new-lines in the markup -->
</defs>
<rect width="100%" height="100%" fill="red" />
<circle cx="150" cy="100" r="80" fill="green" />
<text x="150" y="125" font-size="60" text-anchor="middle" fill="white">SVG</text>
</svg>
just using the dom method
const svg = document.querySelector(`[data-uuid="live_map_svg"]`);
const shape = svg.querySelector(`text`);
const text = shape.innerHTML;
// const text = shape.textContent;
setTimeout(() => {
svg.insertAdjacentHTML(`beforebegin`, text);
}, 1000);
.svg-box{
width: 500px;
height: 500px;
background: #ccc;
color: red;
font-size: 16px;
}
[data-uuid="live_map_svg"]{
font-size: 16px;
}
<div class="svg-box">
<svg
data-uuid="live_map_svg" id="live_map_svg"
width="100%" height="100%"
viewBox="0 0 100 100"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<text x="30" y="50" fill="#369acd">A Area</text>
</svg>
</div>

How do I get an internal SVG element's position relative to the viewport of an outer SVG element?

Say I have an SVG element containing some stuff:
<div style="margin-left:50px; width: 100%; min-height: 400px;">
<svg>
<g transform="translate(34.34,47.5) scale(0.345)" height="100%" width="100%">
<svg x="20" y ="50" style="overflow: visible">
<circle cx="0" cy="0" r="35" stroke="red" fill="blue">
<text>a bunch of text</text>
</svg>
<line />
</g>
<svg>
<div>
I'm trying to find the center position of the <g> relative to the viewport of the outer <svg> element, so that I can translate the <g> to be centered within the outer <svg>, and scale it to fit.
I was able to get it working using getBoundingClientRect() and adjusting for the transform scale, but this does not work in Firefox because the <svg> elements inside the <g> container are not constrained to the bounding box of the displayed section of their contents (rather it's the same size as the outer <svg>, with some scaling).
There is probably a solution using createSVGPoint() and getScreenCTM() or getCTM() but frankly I'm not sure what I should be using.
An SVG without a viewBox attribute has a width of 300px and a height of 150px. I've added a viewBox="0 0 300 150". You can remove it.
Also I've added a rectangle to be able to see the position and the size of the <g> element. You can remove it as well.
How I would center the <g> element: Since the <g> element is transformed the easiest way to get it's size and position would be wrapping the <g> element in another one, in this case <g id="wrap"> Next I can get the bounding box of the wrap: wrap.getBBox()
In order to center the wrap I need to know the center of the main svg canvas: x = 300/2; y=150/2. Now I can translate the wrap into the center
let c = {x:150,y:75}//the center of the main svg element
let bb = wrap.getBBox()//the bounding box of the wrap
let transformation = `translate(${c.x - bb.x - bb.width/2},
${c.y - bb.y - bb.height/2})`
wrap.setAttributeNS(null,"transform",transformation)
svg{border:1px solid;width:100vh;}
text{fill:black;}
path{fill:none;stroke:black}
<div style="margin-left:50px; width: 100%; min-height: 400px;">
<svg id="main" viewBox="0 0 300 150" >
<g id="wrap">
<rect x="29.165" y="47.5" width="45.03" height="29.325" fill="gold" fill-opacity=".5" />
<g transform="translate(34.34,47.5) scale(0.345)" height="100%" width="100%">
<svg x="20" y ="50" style="overflow: visible">
<circle cx="0" cy="0" r="35" stroke="red" fill="blue"/>
<text>a bunch of text</text>
</svg>
<line />
</g>
</g>
<path d="M0,0L300,150M0,150L300,0" />
<svg>
<div>
I hope this is what you were asking.
I managed to figure out a solution using one of the d3.zoom transform methods (we're using d3.zoom to manage the translate/scale transform), and SVGElement.getBBox(). I originally was using this method but had messed up the calculation; this way it works though.
const selection = d3.select(group);
const zoomBehavior = d3.zoom().on('zoom', () => {
selectionTransform = d3.event.transform;
});
selection.call(zoomBehavior);
const scaleAndTransformTo = () => {
selection.call(zoomBehavior.translateBy, Math.random() * 100, Math.random() * 150);
group.setAttribute("transform", selectionTransform.toString());
}
scaleAndTransformTo();
reset.addEventListener('click', scaleAndTransformTo);
run.addEventListener('click', () => {
const { width: containerWidth, height: containerHeight } = container.getBoundingClientRect();
const containerCenter = [containerWidth / 2, containerHeight / 2];
const { height, width, x, y } = group.getBBox();
const nodeBBoxCenter = [x + (width / 2), y + (height / 2)];
// Apply the current interpolated translate/scale to the BBox center to get the actual position
const groupCenterCoords = selectionTransform.apply(nodeBBoxCenter);
const translationOffset = [
(containerCenter[0] - groupCenterCoords[0]) / selectionTransform.k,
(containerCenter[1] - groupCenterCoords[1]) / selectionTransform.k,
];
selection.call(zoomBehavior.translateBy, ...translationOffset);
group.setAttribute("transform", selectionTransform.toString());
});
#page {
display: flex;
flex-direction: column;
position: relative;
align-items: stretch;
margin-left: 100px;
}
#container {
background-color: grey;
flex-grow: 1;
flex-shrink: 0;
min-height: 500px;
border: 1px solid red;
}
#group > svg {
overflow: visible;
}
#group > svg > circle {
overflow: visible;
}
text {
fill: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="page">
<div>
<button id="run">Run</button>
<button id="reset">Reset</button>
</div>
<svg id="container">
<g x="0" y="0" id="group" width="100%" height="100%">
<line x1="20" y1="50" x2="150" y2="150" stroke="brown" />
<svg x="20" y ="50">
<circle cx="0" cy="0" r="35" stroke="red" fill="blue">
<text x="35" y="0" height="100%" width="100%">a bunch of text</text>
</svg>
<line x1="100" y1="350" x2="160" y2="340" stroke="brown" />
<svg x="100" y ="350">
<circle cx="0" cy="0" r="35" stroke="red" fill="blue">
<text x="35" y="0" height="100%" width="100%">a bunch of text 3</text>
</svg>
</g>
<svg>
<div>

SVG text and image centered within a rectangle

I'm really not familiar with SVG's so I'm sorry if this is actually a fairly easy problem..
I'm creating an SVG:
<svg height="100%" width="100%">
<rect x="0" y="0" width="100%" height="100%" fill="#da552f"></rect>
<image xlink:href="https://d30y9cdsu7xlg0.cloudfront.net/noun-svg/703414.svg?Expires=1481685113&Signature=hsa76aA6t5W6xisN8bYKk5t74cmOzTXmYUObaVwE0hUso99Gb4czprrsQAtkaC0aOQJBhNfAn8MjRpKyu8M~AzS5OS3rthGOLFqa3Pk2lCwAWjs-KtTa9fSo7w-sZSJwG6LDeRm5B6T5hYnoKQLibJzCtHvSdUYlp5XBUx1RNvs_&Key-Pair-Id=APKAI5ZVHAXN65CHVU2Q" transform="translate(-35.5,-31)" x="50%" y="50%" height="50px" width="50px"/>
<text fill="#ffffff" x="50%" y="50%" alignment-baseline="middle" text-anchor="middle" font-size="48" font-family="Verdana">Kitty Cat</text>
</svg>
And as you can see both the image of the cat and the text are centered in the rectangle, but this isn't the desired effect I want.
I'd like the image to be next to the text and both of them be centered in the rectangle.. example:
How is this doable using SVGs? Is javascript required? Any help would be great! Thanks
Unlike HTML, SVG has no automatic layout of groups of elements.
You have two choices:
Cheat and put your image and text in HTML and use a <foreignObject> element to embed the HTML in your SVG. Although it is barely an SVG any more. And that only works in browsers.
Use Javascript to measure the text and then re-position it in the centre.
function reCentre() {
var svg = document.getElementById("mysvg");
var group = document.getElementById("centreMe");
// Get the bounding box of the image+text group
var groupBounds = group.getBBox();
// Get the size of the SVG on the page
var svgBounds = svg.getBoundingClientRect();
// Calculate new position for the group
var groupPosX = (svgBounds.width - groupBounds.width) / 2;
var groupPosY = (svgBounds.height - groupBounds.height) / 2;
// Calculate the difference between the groups current position
// and where it needs to be in order to be centred.
var dx = groupPosX - groupBounds.x;
var dy = groupPosY - groupBounds.y;
// Give the group a translate transform to move it to this new position
group.setAttribute("transform", "translate("+dx+","+dy+")");
}
// Initial centering
reCentre();
// Also recentre when window resizes
window.addEventListener("resize", reCentre);
<svg id="mysvg" height="100%" width="100%">
<rect x="0" y="0" width="100%" height="100%" fill="#da552f"></rect>
<g id="centreMe">
<image xlink:href="https://d30y9cdsu7xlg0.cloudfront.net/noun-svg/703414.svg?Expires=1481685113&Signature=hsa76aA6t5W6xisN8bYKk5t74cmOzTXmYUObaVwE0hUso99Gb4czprrsQAtkaC0aOQJBhNfAn8MjRpKyu8M~AzS5OS3rthGOLFqa3Pk2lCwAWjs-KtTa9fSo7w-sZSJwG6LDeRm5B6T5hYnoKQLibJzCtHvSdUYlp5XBUx1RNvs_&Key-Pair-Id=APKAI5ZVHAXN65CHVU2Q" x="0" y="-50" height="50px" width="50px"/>
<text fill="#ffffff" x="80" y="0" font-size="48" font-family="Verdana">Kitty Cat</text>
</g>
</svg>
Edit the x and y attributes of the image tag until the cat face is where you would like it.
<svg height="100%" width="100%">
<rect x="0" y="0" width="100%" height="100%" fill="#da552f"></rect>
<image xlink:href="https://d30y9cdsu7xlg0.cloudfront.net/noun-svg/703414.svg?Expires=1481685113&Signature=hsa76aA6t5W6xisN8bYKk5t74cmOzTXmYUObaVwE0hUso99Gb4czprrsQAtkaC0aOQJBhNfAn8MjRpKyu8M~AzS5OS3rthGOLFqa3Pk2lCwAWjs-KtTa9fSo7w-sZSJwG6LDeRm5B6T5hYnoKQLibJzCtHvSdUYlp5XBUx1RNvs_&Key-Pair-Id=APKAI5ZVHAXN65CHVU2Q" transform="translate(-35.5,-31)" x="25%" y="45%" height="50px" width="50px"/>
<text fill="#ffffff" x="50%" y="50%" alignment-baseline="middle" text-anchor="middle" font-size="48" font-family="Verdana">Kitty Cat</text>
</svg>
you could use a webfont or an emoji instead of an image...
svg {
position: absolute;
top: 0px;
left: 0px;
bottom: 0px;
right: 0px
}
<svg viewBox="0 0 400 200">
<rect x="0" y="0" width="400" height="200" fill="pink" />
<text x="200" y="100" dominant-baseline="middle" text-anchor="middle" font-size="30" font-family="sans serif">
<tspan font-size="50">🐱</tspan>Kitty Cat</text>
</svg>

Detect which element is moused over and pass to function

I'm using greensock to animate an svg clippath, and it works great with one clippath, and hardcoded variables. Now I'm needing to add more clippaths, and I need each one to animate independently. So I need to build some sort of function to detect which circle is being moused over/ moused out and then call the timeline, passing it the correct parameters (the clippath and overlay circle). I'm sure I could probably do that with 'this' but I'm still at the point where 'this' makes my brain melt. Here's the codepen I'm working on.
http://codepen.io/kathryncrawford/pen/JYvdzx
HTML
<svg class="svg-graphic" width="500" height="500" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" version="1.1">
<defs>
<clipPath id="clippath">
<circle id="clip1" cx="200" cy="200" r="2.5"/>
<circle id="clip2" cx="400" cy="200" r="3.2"/>
</clipPath>
</defs>
<image class="svg-image1" xlink:href="http://lorempixel.com/300/300/" width="300" height="300" x="80" y="80"/>
<circle id="circle1" fill="#CC66FF" cx="200" cy="200" r="30"/>
<image class="svg-image2" xlink:href="http://lorempixel.com/300/300/" width="300" height="300" x="380" y="80"/>
<circle id="circle2" fill="#CC66FF" cx="400" cy="200" r="30"/>
JS
var clip = document.getElementById("clip1");
var circles = document.getElementById("circle1");
circles.addEventListener("mouseenter", expand);
circles.addEventListener("mouseleave", contract);
var tl = new TimelineMax({paused: true});
tl.to(clip, 0.5, {
attr: {
r: 120
},
transformOrigin: "50% 50%",
ease: Power4.easeInOut
})
.to(circles, 0.5, {alpha:0, ease:Power4.easeInOut}, '-0.1');
function expand() {
tl.play();
}
function contract() {
tl.reverse();
}
All right, this is what I was able to create by forking your pen.
And here is what has changed:
In HTML, I have removed unique IDs set on each of the circle HTML elements present inside the clipPath HTML element i.e. clipPath's children. Instead, I have given all of these circle tags a clip class.
The other circle elements that are siblings of the said clipPath i.e. present on the same level as clipPath element, have been given a circle class.
And as for the image elements, I have done similar thing. Removed unique IDs from them and instead gave them a common svg-image class.
This is HTML done.
In HTML, since the unique IDs have been removed such as #circle1, #circle2, #svg-image1 and #svg-image2, I have removed them from CSS as well and instead applied exactly the same rules they had on the newly created classes i.e. .circle and .svg-image respectively.
In JavaScript, the clip and circle elements as well as the total number of clip elements are first stored in variables clips, circles and numClips respectively.
There is also an empty array of timelines created initially.
Then there is a loop being initiated which goes up till the length of numClips and which does two things:
createTimeline as the name suggests, is supposed to create a TimelineMax instance which looks similar to what you previously had i.e. it adds two tweens, one for animating the opacity on the current circle element (remember, we are inside a loop and we have a reference of current circle element by the use of circles[i]) and the other for animating r of the current clip element.
assignListeners is for listening to mouseenter and mouseleave events on each of the circle elements.
And finally, expand and collapse methods are for playing or reversing the current timeline instance. (again, we have the reference of the timeline that should be playing when hovered or out using timelines[i] reference).
HTML:
<svg class="svg-graphic" width="500" height="500" xmlns="http://www.w3.org/2000/svg" xlink="http://www.w3.org/1999/xlink" version="1.1">
<defs>
<clipPath id="clippath">
<circle class="clip" cx="200" cy="200" r="20" />
<circle class="clip" cx="400" cy="200" r="20" />
<circle class="clip" cx="600" cy="200" r="20" />
</clipPath>
</defs>
<image class="svg-image" xlink:href="http://lorempixel.com/300/300/" width="300" height="300" x="80" y="80" />
<circle class="circle" fill="#CC66FF" cx="200" cy="200" r="20" />
<image class="svg-image" xlink:href="http://lorempixel.com/300/300/" width="300" height="300" x="380" y="80" />
<circle class="circle" fill="#CC66FF" cx="400" cy="200" r="20" />
<image class="svg-image" xlink:href="http://lorempixel.com/300/300/" width="300" height="300" x="680" y="80" />
<circle class="circle" fill="#CC66FF" cx="600" cy="200" r="20" />
</svg>
CSS:
*{
box-sizing: border-box;
}
body{
margin: 0;
padding: 0
}
.circle{
position: absolute;
margin: 0;
z-index: 1;
clip-path: url("#clippath");
}
.svg-image {
z-index: 3;
clip-path: url(#clippath);
}
svg{
overflow: visible;
}
.svg-graphic {
position: absolute;
}
.imgContainer {
position: relative;
width: 800px;
height: 800px;
}
JavaScript:
var clips = document.getElementsByClassName('clip');
var circles = document.getElementsByClassName('circle');
var numClips = clips.length;
var timelines = [];
for (var i = 0; i < numClips; i += 1) {
createTimeline(i);
assignListeners(i);
}
function createTimeline(i) {
var timeline = new TimelineMax({ paused: true });
timeline.to(circles[i], 0.6, { opacity: 0, ease: Expo.easeInOut }, 0);
timeline.to(clips[i], 0.6, {
attr: { r: 120 },
transformOrigin: '50% 50%',
ease: Expo.easeInOut
}, 0.1);
timelines[i] = timeline;
}
function assignListeners(i) {
(function(i) {
circles[i].addEventListener('mouseenter', function(e) { expand(e, i); }, false);
circles[i].addEventListener('mouseleave', function(e) { contract(e, i); }, false);
}(i));
}
function expand(e, i) { timelines[i].play(); }
function contract(e, i) { timelines[i].reverse(); }
Hope this helps.

Accessing SVG file directly from Javascript code

I have this HTML code, which is invoking my javascript code. The code is for a gauge. In the javascript code, I am trying to access a SVG file, and modifying the needle (of the gauge) to display the desired value. The code is working fine. However, I do not wish to call "object id" in HTML. I want to access SVG file through javascript directly, instead of using object id in HTML. I tried using el.setAttribute('data', 'gauge.svg'); But then svg_doc isn't able to retrieve the SVG image and modify the needle. Any help would be highly appreciated.
PS : I tried my best to be as thorough in explaining the problem. However, please let me know if I am unclear somewhere.
This is Gauge.png image which is embedded in the svg code I have pasted below https://sphotos-b.xx.fbcdn.net/hphotos-snc6/179594_10150982737360698_1827200234_n.jpg
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g name="gauge" width="122px" height="127px">
<image xlink:href="gauging.png" width="122" height="127"/>
<circle id="led" cx="39" cy="76" r="5" style="fill: #999; stroke: none">
<animateColor id="ledAnimation" attributeName="fill" attributeType="css" begin="0s" dur="1s"
values="none;#f88;#f00;#f88;none;" repeatCount="0"/>
</circle>
<g id="needle" transform="rotate(0,62,62)">
<circle cx="62" cy="62" r="4" style="fill: #c00; stroke: none"/>
<rect transform="rotate(-130,62,62)" name="arrow" x="58" y="38" width="8" height="24" style="fill: #c00; stroke: none"/>
<polygon transform="rotate(-130,62,62)" points="58,39,66,39,62,30,58,39" style="fill: #c00; stroke: none"/>
</g>
<text id="value" x="51" y="98" focusable="false" editable="no" style="stroke:none; fill:#fff; font-family: monospace; font-size: 12px"></text>
</g>
</svg>
HTML+Javascript code
<head>
<title>SVG Gauge example</title>
<script>
function update1(){
var scale=100;
var value;
var value1 = 69;
var el=document.getElementById('gauge1');
if (!el) return;
/* Get SVG document from HTML element */
var svg_doc = el.contentDocument;
if (!svg_doc) return;
/* Rotate needle to display given value */
var needle_el = svg_doc.getElementById('needle');
if (!needle_el) return;
/* Calc rotation angle (0->0%, 260->100%) */
value = parseInt(value1);
scale = parseInt(scale);
if (value > scale) value = scale;
var angle = value / scale * 260;
/* On-the-fly SVG transform */
needle_el.setAttribute('transform','rotate('+angle+',62,62)');
}
document.addEventListener('load', update1, true);
</script>
</head>
<div>
<object id="gauge1" type="image/svg+xml" data="gauge.svg" width="127" height="122"/>
</div>
</html>
As robertc already mentioned, you can embed the javascript code into your SVG file:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g name="gauge" width="122px" height="127px">
<image xlink:href="gauging.png" width="122" height="127"/>
<circle id="led" cx="39" cy="76" r="5" style="fill: #999; stroke: none">
<animateColor id="ledAnimation" attributeName="fill" attributeType="css" begin="0s" dur="1s"
values="none;#f88;#f00;#f88;none;" repeatCount="0"/>
</circle>
<g id="needle" transform="rotate(0,62,62)">
<circle cx="62" cy="62" r="4" style="fill: #c00; stroke: none"/>
<rect transform="rotate(-130,62,62)" name="arrow" x="58" y="38" width="8" height="24" style="fill: #c00; stroke: none"/>
<polygon transform="rotate(-130,62,62)" points="58,39,66,39,62,30,58,39" style="fill: #c00; stroke: none"/>
</g>
<text id="value" x="51" y="98" focusable="false" editable="no" style="stroke:none; fill:#fff; font-family: monospace; font-size: 12px"></text>
</g>
<script type="text/javascript">
var scale=100;
var value;
var value1 = 69;
/* Rotate needle to display given value */
var needle_el = document.getElementById('needle');
/* Calc rotation angle (0->0%, 260->100%) */
value = parseInt(value1);
scale = parseInt(scale);
if (value > scale) value = scale;
var angle = value / scale * 260;
/* On-the-fly SVG transform */
needle_el.setAttribute('transform','rotate('+angle+',62,62)');
</script>
</svg>
I've put the code below the actual SVG contents so that the document is already loaded when the script is executed.
Then, you can view the SVG file directly e.g. in Firefox (I've tested it right now).

Categories

Resources