More SVG questions (and Javascript) - Interactivity [moving a rectangle] - javascript

The following code is supposed to make the rectangle move when pressing the WASD keys on the keyboard. It doesn't work. The rectangle does not move, but no errors are generated. How can I make this work?
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="90%" height="90%" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<script type="text/javascript">
<![CDATA[
var x;
var y;
function move()
{
x = new Number(document.getElementById("player").x);
y = new Number(document.getElementById("player").y);
switch (event.keyCode)
{
case 119:
y--;
document.getElementById("player").y=y;
break;
case 115:
y++;
document.getElementById("player").y=y;
break;
case 97:
x--;
document.getElementById("player").x=x;
break;
case 100:
x++;
document.getElementById("player").x=x;
break;
default:
}
}
document.documentElement.addEventListener("keypress", move, true);
]]>
</script>
<rect x="0" y="0" height="100%" width="100%" style="stroke-width:5; stroke:black; fill:white"></rect>
<rect x="250" y="250" id="player" height="50" width="50" style="stroke-width:1; stroke:red; fill:red"></rect>
</svg>

Your code above does not work because the x and y properties of an SVGRectElement are SVGAnimatedLength objects, representing values that can change over time.
The simplest changes to make your code 'work' are:
Change
x = new Number(document.getElementById("player").x);
to
x = new Number(document.getElementById("player").x.baseVal.value);
(and similarly for y).
Change
document.getElementById("player").x=x;
to
document.getElementById("player").x.baseVal.value=x;
(and similarly for y).
This will cause your code to work as desired. However, your code could generally be improved per my answer to your previous question.
Here is how I would write it (if you really only want to move one unit at a time):
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="90%" height="90%" version="1.1"
xmlns="http://www.w3.org/2000/svg">
<style>
#player { stroke-width:1; stroke:red; fill:red }
</style>
<rect x="250" y="250" id="player" height="50" width="50" />
<script type="text/javascript"><![CDATA[
var KEY = { w:87, a:65, s:83, d:68 };
var rect = document.getElementById('player');
var x = rect.getAttribute('x')*1,
y = rect.getAttribute('y')*1;
document.documentElement.addEventListener("keydown", function(evt){
switch (evt.keyCode){
case KEY.w: rect.y.baseVal.value = --y; break;
case KEY.s: rect.y.baseVal.value = ++y; break;
case KEY.a: rect.x.baseVal.value = --x; break;
case KEY.d: rect.x.baseVal.value = ++x; break;
}
}, false);
]]></script>
</svg>
Note that I've moved the <rect> above the <script> block so that a) the user sees the rectangle before the script starts to load, but more importantly b) so that the rectangle exists before calling getElementById(). If your script block occurs before your elements have been defined, you must instead set up your code references in response to an onload or similar event handler.

Related

Change Position of svg element using JS

Trying to make an svg rectangle that moves when button pushed. Right now I just want the x to be modified by a function.
function modX()
{
document.getElementById("rectangle").transform = 'translate(295 115)';
}
var x = 20;
var y = 20;
modX();
<svg width="1000" height="1000" >
<rect id="rectangle" x="0" y="20" width="100" height="100"
style="fill:rgb(0,0,255);stroke-width:3;stroke:rgb(0,0,0)"></rect>
</svg>
I'm fairly new to code so please avoid css or jquery.
you can change its x by using javaScript document.getElementById("rectangle").setAttribute('x', X_value)
set the X_value to the value you want it to change.

How to interrogate snap svg event object to get viewbox coordinate?

I have a simple svg file containing 2 rectangles and a text :
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg id="mySvg" onload="init()" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" height="100%" width="100%" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" viewBox="0 0 1371.4286 773.5047">
<script xlink:href="snap.svg-min.js" type="text/ecmascript"/>
<rect id="blackRect" style="color-rendering:auto;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;image-rendering:auto" ry=".16344" height="773.5" width="1371.4" y="161.71" x="-371.43" fill="#ffb380"/>
<rect id="hoverTarget" style="color-rendering:auto;color:#000000;isolation:auto;mix-blend-mode:normal;shape-rendering:auto;solid-color:#000000;image-rendering:auto" ry=".098402" height="177.14" width="286.62" y="369.51" x="180" fill="#008080"/>
<text id="viewboxText" style="word-spacing:0px;letter-spacing:0px" font-weight="bold" xml:space="preserve" font-size="27.5px" line-height="125%" y="810.93359" x="-3.165039e-005" font-family="sans-serif" fill="#000000"><tspan id="tspan3348" x="-3.165039e-005" y="810.93359">Viewbox :</tspan></text>
</svg>
and before the closing </svg> tag i added this script:
<script><![CDATA[
var mySvg, hoverTarget, viewboxText;
function init(){
mySvg=Snap("#mySvg");
hoverTarget=mySvg.select("#hoverTarget");
viewboxText=mySvg.select("#viewboxText");
hoverTarget.mousemove(hoverCursor);
}
function hoverCursor(evt){
var cursorX, cursorY;
if(evt.type==="mousemove"){
cursorX=...;
/*this is my question : how to interrogate the evt object to get X position of the cursor
according to mySvg viewbox?*/
cursorY=...;
//and, of course, how to get Y position from the evt object..
//and after that, display it on the text...
viewboxText.attr({text : "X : " + cursorX + ", Y : " + cursorY});
}
}
]]></script>
My question is : is there any way to interrogate snap event object of the event handler function to obtain viewbox coordinate of the svg parent element?
If there is, what is the syntax?
This is what I think you may be after...
First we get the inverse matrix from the element to the screen
var m = Snap.matrix( evt.target.getScreenCTM().inverse() )
// you may want to use this instead, but should be same for this case
// var m = Snap.matrix( mySvg.node.getScreenCTM().inverse() )
var newX = m.x( cursorX, cursorY )
var newY = m.y( cursorX, cursorY )
m.x(), m.y() convert the x,y coordinates with the new matrix.
jsfiddle

how to scale the element created by use by keeping the fixed position in 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 getIntersectionList returns null

In the context of a SVG file, the following JavaScript:
var svg = document.rootElement, hitRect, hits;
hitRect = svg.createSVGRect();
hitRect.height = 1;
hitRect.width = 1;
hitRect.y = 100;
hitRect.x = 100;
hits = svg.getIntersectionList(hitRect, null);
always assigns null to hits, regardless if there were any intersections at all (in the case of no intersections, it should've been an empty NodeList).
Has anyone stumbled in to this problem? Is there a known workaround for hit-testing a SVG in Android?
Tested on: Android default browser on Android 4.0.3 (emulator), 4.0.3 (GALAXY Note SC-05D). (Google Chrome works)
Edit
I also tried looping through all elements (document.getElementsByTagName("*")), testing each one with svg.checkIntersection, to no avail. checkIntersection just returned true for every single element.
I also encounter the problem when test of iOS Safari <= 5.0, same like it's not support SVG 1.1, so after adding following attribute to the svg element, it's no effect:
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect id="r1" x="5" y="5" width="40" height="40"/>
<rect id="r2" x="105" y="105" width="40" height="40"/>
<rect id="r3" x="5" y="5" width="40" height="40"/>
<rect id="rr" x="5" y="5" width="400" height="400" fill="red"/>
<rect id="r4" x="5" y="5" width="40" height="40"/>
<rect id="r5" x="5" y="5" width="40" height="40"/>
</svg>
<script type="text/javascript">
var isTouch = ('ontouchstart' in window) || ('DocumentTouch' in window && document instanceof DocumentTouch);
var downE = isTouch? 'touchstart' :'mousedown';
var moveE = isTouch? 'touchmove' :'mousemove';
var upE = isTouch? 'touchend' :'mouseup';
window.addEventListener(downE, startTest )
function startTest (evt) {
setTimeout(function () {
var svg=document.querySelector('svg');
var r = svg.createSVGRect();
r.x = 20;
r.y = 20;
r.width = r.height = 44;
var getIntersectionList = svg.getIntersectionList(r, null );
var checkIntersection = svg.checkIntersection( document.querySelector('#r2'), r );
var elementFromPoint = document.elementFromPoint(20,20);
alert("getIntersectionList: "+getIntersectionList);
alert("checkIntersection: "+checkIntersection);
alert("elementFromPoint: "+elementFromPoint);
},1000);
}
</script>
</body>
</html>
Above test code should alert:
getIntersectionList: [object NodeList]
checkIntersection: false
elementFromPoint: [object SVGRectElement]
But in iOS Safari <=5.0 and some android it's alert:
getIntersectionList: null
checkIntersection: true
elementFromPoint: [object SVGRectElement]
So, if you want to get better compatibility, only rely on the method:
var el = document.elementFromPoint(x,y);
it's get topmost element under the point (x,y), even support IE 5.5
You can test 5 points in a small rect area(center, 4 corner) (or more points), to workaround this.

Including JavaScript in SVG

I am trying to create an interactive SVG code with JavaScript, by embedding the JavaScript in the SVG. I don't know if this is the right way to do this:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" version="1.1"
xmlns="http://www.w3.org/2000/svg"
onkeypress="move()">
<script type="text/javascript">
<![CDATA[
var x;
var y;
function move()
{
x = new Number(svg.getElementsByTagName("circle")[0].getAttribute("cx"));
y = new Number (svg.getElementsByTagName("circle")[0].getAttribute("cy"));
switch (event.keyCode)
{
case 119:
y--;
y = y.toString();
svg.getElementsByTagName("circle").setAttribute("cy",y);
break;
case 115:
y++;
y = y.toString();
svg.getElementsByTagName("circle").setAttribute("cy",y);
break;
case 97:
x--;
x = x.toString();
svg.getElementsByTagName("circle").setAttribute("cx",x);
break;
case 100:
x++;
x = x.toString();
svg.getElementsByTagName("circle").setAttribute("cx",x);
break;
default:
}
}
]]>
</script>
<rect x="0" y="0" height="500" width="500" style="stroke-width:1; stroke:black; fill:white"></rect>
<circle cx="250" cy="250" r="50" stroke="red" stroke-width="1" fill="red"></circle>
</svg>
It is supposed to have a ball that moves with wasd, but the ball doesn't move. What am I doing wrong?
Here is a working version as I would write it:
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
<circle cx="250" cy="250" r="50" fill="red" />
<script type="text/javascript"><![CDATA[
var KEY = { w:87, a:65, s:83, d:68 };
var moveSpeed = 5;
var circle = document.getElementsByTagName("circle")[0];
var x = circle.getAttribute('cx')*1,
y = circle.getAttribute('cy')*1;
document.documentElement.addEventListener('keydown',function(evt){
switch (evt.keyCode){
case KEY.w:
circle.setAttribute('cy',y-=moveSpeed);
// Alternatively:
// circle.cy.baseVal.value = (y-=moveSpeed);
break;
case KEY.s:
circle.setAttribute('cy',y+=moveSpeed);
break;
case KEY.a:
circle.setAttribute('cx',x-=moveSpeed);
break;
case KEY.d:
circle.setAttribute('cx',x+=moveSpeed);
break;
}
},false);
]]></script>
</svg>
Some notes:
Don't re-get the reference to the circle again and again. Making your code DRY makes it more robust, less typing, and (in this case) faster to execute.
Edit: If you cannot figure out how to do this given my code above, post any code that is not working for you.
Don't rely on a global event object; that's old IE nonsense. Use the event object passed to your event handler.
Edit: If you reference event in your code with no parameter or local variable by that name, you are assuming that there will be a global event object set. Instead, see the code I wrote for you, which shows that the event handler is passed an event object. By giving that a name, such as I gave it the name evt, you are receiving an event object specific to your event handler.
Since you are modifying the x and y variables, there's no need to re-get the cx and cy attributes each key press.
Edit: In your original code and the answer you accepted, you have declared var x outside your event handler, and you have x = ... at the start of your event handler, and then x++ in one of the event handlers. You can either re-get the current value of x each time (as you did) and then setAttribute(...,x+1), or (as I did) you can only fetch the value of the attribute once before the event handlers and then assume that this value is correct each time you handle the key event.
Don't put your JavaScript event handlers on your elements, attach them programmatically.
Edit: In your SVG markup you have: <svg ... onkeypress="move()">. Mixing your behavior with your markup is a really bad idea in HTML, and a bad idea in SVG. Instead of using onfoo="..." attributes to describe what should happen when an event occurs on an element, instead use addEventListner() to attach the event handlers via code, without editing your SVG markup.
There's no need to coerce the numbers to strings before setting them as attributes.
Use keydown and the ASCII event codes I supplied above instead of keypress and the odd numbers you were using if you want it to work in all browsers.
Edit: You complained in another post that you cannot do this because you want the event handler to be processed repeatedly as the key is held down. Note that your desired behavior is achieved with my sample code in Chrome, Safari, Firefox, and IE (I don't have Opera to test). In other words keydown works as you wanted, despite how you thought it should behave.
Edit 2: If you want to include a script block at the top of your document, before all elements have necessarily been created, you can do something like the following:
<svg ...>
<script type="text/javascript">
window.addEventListener('load',function(){
var circle = ...;
document.rootElement.addEventListener('keydown',function(evt){
...
},false);
},false);
</script>
...
</svg>
The outer function will only run once the page has loaded, so you can be sure that the elements exist to reference them.
you can add script js into svg code by declare script tag into svg tag:
<svg version="1.1" id="loader-1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="40px" height="40px" viewBox="0 0 50 50" style="enable-background:new 0 0 50 50;" xml:space="preserve">
...
<script type="text/javascript">
window.addEventListener('load',function(){
alert('Hi')
})
</script>
</svg>
This works in Chrome. You had a few errors, like indexing getElementsByTagName only sometimes. Plus the big problem was that the onkeypress attribute wasn't binding.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" version="1.1"
xmlns="http://www.w3.org/2000/svg"
>
<script type="text/javascript">
<![CDATA[
var x;
var y;
function move()
{
x = new Number(document.getElementsByTagName("circle")[0].getAttribute("cx"));
y = new Number (document.getElementsByTagName("circle")[0].getAttribute("cy"));
switch (event.keyCode)
{
case 119:
y--;
y = y.toString();
document.getElementsByTagName("circle")[0].setAttribute("cy",y);
break;
case 115:
y++;
y = y.toString();
document.getElementsByTagName("circle")[0].setAttribute("cy",y);
break;
case 97:
x--;
x = x.toString();
document.getElementsByTagName("circle")[0].setAttribute("cx",x);
break;
case 100:
x++;
x = x.toString();
document.getElementsByTagName("circle")[0].setAttribute("cx",x);
break;
default:
}
}
document.documentElement.addEventListener("keypress", move);
]]>
</script>
<rect x="0" y="0" height="500" width="500" style="stroke-width:1; stroke:black; fill:white"></rect>
<circle cx="250" cy="250" r="50" stroke="red" stroke-width="1" fill="red"></circle>
</svg>
Simple Example
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<path d="M90,18c-90-45-115,102,0,69v-21l4-3h-23l-8,4h16v19c-80,15-65-106,2-63l-4,5l4-1z" fill="#CCC" stroke="#DDD" stroke-width="2" stroke-linejoin="round"/>
<path d="M87,15c-90-45-115,102,0,69v-21l4-3h-23l-8,4h16v19c-80,15-65-106,2-63l-4,5l4-1z" fill="#00F"/>
<script>
alert("Hello world");
</script>
</svg>

Categories