I have a SVG file and a JavaScript file.
In my SVG file I have some code, but it is this small amount of code I'm wondering about:
<g id="zoomButton">
<g onclick="zoom()" transform="scale(x,y)">
<rect x="640" y="20" width="140" height="40"
style="fill:white;stroke:red;stroke-width:2" />
<text x="710" y="49"
style="fill:red;font-size:25px;text-anchor:middle">Zoom</text>
</g>
</g>
You see that I want the function zoom() to be called when this button is clicked.
Now, in my JavaScript file, I have this function called zoom():
function zoom() {
alert("heyho");
}
However, this function is not triggered when I press the button created in the SVG file. I only get the error message:
Uncaught ReferenceError: zoom is not defined
I just want to be able to make that JavaScript function to be called when I press this button.
Furthermore, since this is a zoom-function after all and I want to do some scaling, how do I access the x and y inside transform="scale(x,y) in the zoomButton in the SVG file from the JavaScript file?
Edit:
The full code in the SVG file is as follows (game.svg):
<?xml version="1.0" encoding="utf-8"?>
<svg width="800px" height="600px"
xmlns="http://www.w3.org/2000/svg"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:a="http://www.adobe.com/svg10-extensions" a:timeline="independent"
onload="top.load(evt)">
<defs>
<clipPath id="gameareaclip">
<rect x="20" y="20" width="600" height="560"/>
</clipPath>
<pattern id="background_pattern" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
<rect width="20" height="20" style="fill:purple"/>
<circle cx="10" cy="10" r="8" style="fill:black"/>
</pattern>
<radialGradient id="player_color">
<stop offset="0.0" style="stop-color:yellow;stop-opacity:1"/>
<stop offset="0.8" style="stop-color:yellow;stop-opacity:1"/>
<stop offset="1.0" style="stop-color:orange;stop-opacity:1"/>
</radialGradient>
</defs>
<rect width="100%" height="100%" style="fill:url(#background_pattern);stroke:orange;stroke-width:4" />
<rect x="20" y="20" width="600" height="560" style="fill:black;stroke:red;stroke-width:5" />
<!-- Add your button here -->
<g id="zoomKnapp">
<g onclick="zoom()" transform="scale(x,y)">
<rect x="640" y="20" width="140" height="40"
style="fill:white;stroke:red;stroke-width:2" />
<text x="710" y="49"
style="fill:red;font-size:25px;text-anchor:middle">Zoom</text>
</g>
</g>
<g style="clip-path:url(#gameareaclip)">
<g transform="translate(20,20)">
<g id="gamearea" transform="translate(0,0)" width="600" height="560">
<rect x="0" y="0" width="600" height="560" style="fill:lightgrey" />
<g id="platforms">
<!-- Add your platforms here -->
<rect id="svg_37" height="20" width="300" y="100" x="0" stroke-linecap="null" stroke-linejoin="null" stroke-width="0" stroke="#000000" fill="#5fbf00"/>
<rect id="svg_38" height="20" width="300" y="170" x="300" stroke-linecap="null" stroke-linejoin="null" stroke-width="0" stroke="#000000" fill="#5fbf00"/>
<rect id="svg_39" height="20" width="300" y="240" x="0" stroke-linecap="null" stroke-linejoin="null" stroke-width="0" stroke="#000000" fill="#5fbf00"/>
<rect id="svg_40" height="20" width="300" y="310" x="300" stroke-linecap="null" stroke-linejoin="null" stroke-width="0" stroke="#000000" fill="#5fbf00"/>
<rect id="svg_41" height="20" width="300" y="380" x="0" stroke-linecap="null" stroke-linejoin="null" stroke-width="0" stroke="#000000" fill="#5fbf00"/>
<rect id="svg_42" height="20" width="300" y="450" x="300" stroke-linecap="null" stroke-linejoin="null" stroke-width="0" stroke="#000000" fill="#5fbf00"/>
</g>
<g id="player">
<circle cx="20" cy="20" r="20" style="fill:url(#player_color);stroke:black;stroke-width:2"/>
<ellipse cx="15" cy="15" rx="3" ry="6" style="fill:black"/>
<ellipse cx="25" cy="15" rx="3" ry="6" style="fill:black"/>
<path d="M10,25 l20,0 q0,8 -10,8 t-10,-8" style="fill:orange;stroke:black;stroke-black:2"/>
</g>
</g>
</g>
</g>
</svg>
The HTML file (game.html):
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>SVG Game</title>
<script language="JavaScript" src="game.js"></script>
</head>
<body style="text-align: center">
<embed src="game.svg" type="image/svg+xml" width="800" height="600" />
</body>
</html>
The JavaScript (game.js) code:
// The point and size class used in this program
function Point(x, y) {
this.x = (x)? parseFloat(x) : 0.0;
this.y = (y)? parseFloat(y) : 0.0;
}
function Size(w, h) {
this.w = (w)? parseFloat(w) : 0.0;
this.h = (h)? parseFloat(h) : 0.0;
}
// Helper function for checking intersection between two rectangles
function intersect(pos1, size1, pos2, size2) {
return (pos1.x < pos2.x + size2.w && pos1.x + size1.w > pos2.x &&
pos1.y < pos2.y + size2.h && pos1.y + size1.h > pos2.y);
}
function zoom() {
alert("heiho");
}
// The player class used in this program
function Player() {
this.node = svgdoc.getElementById("player");
this.position = PLAYER_INIT_POS;
this.motion = motionType.NONE;
this.verticalSpeed = 0;
}
Player.prototype.isOnPlatform = function() {
var platforms = svgdoc.getElementById("platforms");
for (var i = 0; i < platforms.childNodes.length; i++) {
var node = platforms.childNodes.item(i);
if (node.nodeName != "rect") continue;
var x = parseFloat(node.getAttribute("x"));
var y = parseFloat(node.getAttribute("y"));
var w = parseFloat(node.getAttribute("width"));
var h = parseFloat(node.getAttribute("height"));
if (((this.position.x + PLAYER_SIZE.w > x && this.position.x < x + w) ||
((this.position.x + PLAYER_SIZE.w) == x && this.motion == motionType.RIGHT) ||
(this.position.x == (x + w) && this.motion == motionType.LEFT)) &&
this.position.y + PLAYER_SIZE.h == y) return true;
}
if (this.position.y + PLAYER_SIZE.h == SCREEN_SIZE.h) return true;
return false;
}
Player.prototype.collidePlatform = function(position) {
var platforms = svgdoc.getElementById("platforms");
for (var i = 0; i < platforms.childNodes.length; i++) {
var node = platforms.childNodes.item(i);
if (node.nodeName != "rect") continue;
var x = parseFloat(node.getAttribute("x"));
var y = parseFloat(node.getAttribute("y"));
var w = parseFloat(node.getAttribute("width"));
var h = parseFloat(node.getAttribute("height"));
var pos = new Point(x, y);
var size = new Size(w, h);
if (intersect(position, PLAYER_SIZE, pos, size)) {
position.x = this.position.x;
if (intersect(position, PLAYER_SIZE, pos, size)) {
if (this.position.y >= y + h)
position.y = y + h;
else
position.y = y - PLAYER_SIZE.h;
this.verticalSpeed = 0;
}
}
}
}
Player.prototype.collideScreen = function(position) {
if (position.x < 0) position.x = 0;
if (position.x + PLAYER_SIZE.w > SCREEN_SIZE.w) position.x = SCREEN_SIZE.w - PLAYER_SIZE.w;
if (position.y < 0) {
position.y = 0;
this.verticalSpeed = 0;
}
if (position.y + PLAYER_SIZE.h > SCREEN_SIZE.h) {
position.y = SCREEN_SIZE.h - PLAYER_SIZE.h;
this.verticalSpeed = 0;
}
}
//
// Below are constants used in the game
//
var PLAYER_SIZE = new Size(40, 40); // The size of the player
var SCREEN_SIZE = new Size(600, 560); // The size of the game screen
var PLAYER_INIT_POS = new Point(0, 0); // The initial position of the player
var MOVE_DISPLACEMENT = 5; // The speed of the player in motion
var JUMP_SPEED = 15; // The speed of the player jumping
var VERTICAL_DISPLACEMENT = 1; // The displacement of vertical speed
var GAME_INTERVAL = 25; // The time interval of running the game
//
// Variables in the game
//
var motionType = {NONE:0, LEFT:1, RIGHT:2}; // Motion enum
var svgdoc = null; // SVG root document node
var player = null; // The player object
var gameInterval = null; // The interval
var zoom = 1.0; // The zoom level of the screen
//
// The load function for the SVG document
//
function load(evt) {
// Set the root node to the global variable
svgdoc = evt.target.ownerDocument;
// Attach keyboard events
svgdoc.documentElement.addEventListener("keydown", keydown, false);
svgdoc.documentElement.addEventListener("keyup", keyup, false);
// Remove text nodes in the 'platforms' group
cleanUpGroup("platforms", true);
// Create the player
player = new Player();
// Start the game interval
gameInterval = setInterval("gamePlay()", GAME_INTERVAL);
}
//
// This function removes all/certain nodes under a group
//
function cleanUpGroup(id, textOnly) {
var node, next;
var group = svgdoc.getElementById(id);
node = group.firstChild;
while (node != null) {
next = node.nextSibling;
if (!textOnly || node.nodeType == 3) // A text node
group.removeChild(node);
node = next;
}
}
//
// This is the keydown handling function for the SVG document
//
function keydown(evt) {
var keyCode = (evt.keyCode)? evt.keyCode : evt.getKeyCode();
switch (keyCode) {
case "N".charCodeAt(0):
player.motion = motionType.LEFT;
break;
case "M".charCodeAt(0):
player.motion = motionType.RIGHT;
break;
// Add your code here
case "Z".charCodeAt(0):
if (player.isOnPlatform()) {
//Jump
player.verticalSpeed = JUMP_SPEED;
}
break;
}
}
//
// This is the keyup handling function for the SVG document
//
function keyup(evt) {
// Get the key code
var keyCode = (evt.keyCode)? evt.keyCode : evt.getKeyCode();
switch (keyCode) {
case "N".charCodeAt(0):
if (player.motion == motionType.LEFT) player.motion = motionType.NONE;
break;
case "M".charCodeAt(0):
if (player.motion == motionType.RIGHT) player.motion = motionType.NONE;
break;
}
}
//
// This function updates the position and motion of the player in the system
//
function gamePlay() {
// Check whether the player is on a platform
var isOnPlatform = player.isOnPlatform();
// Update player position
var displacement = new Point();
// Move left or right
if (player.motion == motionType.LEFT)
displacement.x = -MOVE_DISPLACEMENT;
if (player.motion == motionType.RIGHT)
displacement.x = MOVE_DISPLACEMENT;
// Fall
if (!isOnPlatform && player.verticalSpeed <= 0) {
displacement.y = -player.verticalSpeed;
player.verticalSpeed -= VERTICAL_DISPLACEMENT;
}
// Jump
if (player.verticalSpeed > 0) {
displacement.y = -player.verticalSpeed;
player.verticalSpeed -= VERTICAL_DISPLACEMENT;
if (player.verticalSpeed <= 0)
player.verticalSpeed = 0;
}
// Get the new position of the player
var position = new Point();
position.x = player.position.x + displacement.x;
position.y = player.position.y + displacement.y;
// Check collision with platforms and screen
player.collidePlatform(position);
player.collideScreen(position);
// Set the location back to the player object (before update the screen)
player.position = position;
updateScreen();
}
//
// This function updates the position of the player's SVG object and
// set the appropriate translation of the game screen relative to the
// the position of the player
//
function updateScreen() {
// Transform the player
player.node.setAttribute("transform", "translate(" + player.position.x + "," + player.position.y + ")");
// Calculate the scaling and translation factors
// Add your code here
}
The script tag needs to go in the SVG file (game.svg) and not in the html file. Note that SVG script tags use xlink:href instead of src as the attribute that holds the script source.
<script xlink:href="game.js"></script>
You should also change onload="top.load(evt)" to just onload="load(evt)" in the root <svg> element.
And your third and main issue is that you have a function zoom() and a variable called zoom on line 114. Remove the variable as it isnt used and the zoom method will get called.
Related
I am trying to have an svg with internal javascript, see here <"https://jsfiddle.net/melvinT/boaus7k1/76/">jsfiddle of svg draggable triangle path it is three circles that drag a triangle. But there is just a bit that stubbornly stop working when I move it into the svg, and I can not figure out why?
The section that mysteriously stops moving when I drag it into the svg is the following (it is at the end) Why? How can I move it into the svg?
var elements = [p1, p2, p3,];
for (i = 0; i < elements.length; i++) {
elements[i].addEventListener('mousedown', onMouseDown);
}
If you put the script element at the bottom of your SVG then it should work because then it will run after the circle elements have been loaded.
<svg id="figure1" width="500" height="500">
<style>
#figure1 {
border: 1px solid blue;
}
</style>
<path d="M170 150 20 110 250 60 z" stroke="pink" fill="none" id="controlPath"/>
<circle cx="170" cy="150" r="8" id="p1" fill="red" style="cursor: move;"/>
<circle cx="20" cy="110" r="8" id="p2" fill="black" style="cursor: move;"/>
<circle cx="250" cy="60" r="8" id="p3" fill="lightgrey" style="cursor: move;"/>
<script type="text/javascript">
<![CDATA[
var svg = document.getElementById('figure1');
svg.addEventListener('mousemove', onMouseMove);
svg.addEventListener('mouseup', onMouseUp);
var controlPath = document.getElementById('controlPath');
var dragElem = null;
var p1 = document.getElementById('p1');
var p2 = document.getElementById('p2');
var p3 = document.getElementById('p3');
var elements = [p1, p2, p3,];
for (i = 0; i < elements.length; i++) {
elements[i].addEventListener('mousedown', onMouseDown);
}
function onMouseMove(ev) {
var p;
if (!dragElem) {
return;
}
p = getClientPointInSVG(ev);
dragElem.setAttribute('cx', p.x - mouseOffsetX);
dragElem.setAttribute('cy', p.y - mouseOffsetY);
controlPath.setAttribute('d',
'M' + p1.getAttribute('cx') + ' ' + p1.getAttribute('cy') +
' ' + p2.getAttribute('cx') + ' ' + p2.getAttribute('cy') +
' ' + p3.getAttribute('cx') + ' ' + p3.getAttribute('cy') +
' ' + 'z' );
}
function onMouseDown(ev) {
var p;
dragElem = ev.target;
p = getClientPointInSVG(ev);
mouseOffsetX = p.x - dragElem.getAttribute('cx');
mouseOffsetY = p.y - dragElem.getAttribute('cy');
}
function getClientPointInSVG(ev) {
var p, m;
p = svg.createSVGPoint();
p.x = ev.clientX;
p.y = ev.clientY;
m = dragElem.getScreenCTM();
return p.matrixTransform(m.inverse());
}
function onMouseUp(ev) {
dragElem = null;
}
var mouseOffsetX, mouseOffsetY;
]]>
</script>
</svg>
<svg id="route "xmlns="http://www.w3.org/2000/svg" version="1.1" width="712px" height="2018px" viewBox="0 0 712 2018" class="hidden-xs hidden-sm">
<path id="triangle" fill="none" stroke="red" stroke-width="5" d="M-3.215,2030.921
c8.816-23.698,1.293-50.813,11.855-74.949c13.239-30.25,36.07-52.368,63.675-68.83c39.115-23.327,200.048-39.239,241.599-39.239 c85.812,0,169.935-1.5,251.667-17.933c74.616-15.001,99.381-62.185,103.271-132.082c7.2-129.404-162.375-182.709-261.459-210.021 c-66.561-18.348-117.888-37.01-175.849-77.998c-40.217-28.439-112.694-81.217-96.218-141.023
c35.773-129.859,256.615-74.554,349.684-99.021c67.062-17.629,147.101-35.616,189.606-92.854
c45.659-61.482,39.673-120.866-5.028-180.841c-37.314-50.064-126.141-67.693-184.915-80.39
c-90.278-19.498-192.558-10.397-279.444-44.865c-74.021-29.363-167.793-73.691-193.513-156.378
c-17.428-56.03-20.716-143.802,36.015-181.982c58.886-39.631,148.526-40.825,216.305-49.425
c71.297-9.047,146.186-9.725,215.652-23.335c74.498-14.597,140.473-30.115,189.976-93.165
c32.844-41.83,53.193-89.854,24.771-156.016C641.086-13.609,557.179,7.892,440.83,7.892"/>
</svg>
Above is my SVG code its simple curve line path.
And my javascript code I want to run, but it draws from middle:
var triangle = document.getElementById("triangle");
var length = triangle.getTotalLength();
triangle.style.strokeDasharray = length;
triangle.style.strokeDashoffset = length;
window.addEventListener("scroll", myFunction);
function myFunction() {
var scrollpercent = (document.body.scrollTop + document.documentElement.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
var draw = length * scrollpercent;
triangle.style.strokeDashoffset = length - draw;
}
In order to make it begin at the top I've reversed the path. I hope this is what you need.
var triangle = document.getElementById("triangle");
var length = triangle.getTotalLength();
triangle.style.strokeDasharray = length;
triangle.style.strokeDashoffset = length;
window.addEventListener("scroll", myFunction);
function myFunction(e) {
var scrollpercent = (document.body.scrollTop + document.documentElement.scrollTop) / (document.documentElement.scrollHeight - document.documentElement.clientHeight);
var draw = length * scrollpercent;
triangle.style.strokeDashoffset = length - draw;
}
<svg id="route "xmlns="http://www.w3.org/2000/svg" version="1.1" width="712px" height="2018px" viewBox="0 0 712 2018" class="hidden-xs hidden-sm">
<path id="triangle" fill="none" stroke="red" stroke-width="5" d="M440.83,7.892C557.179,7.892 641.086,-13.609 694.435,110.574C722.857,176.736 702.508,224.76 669.664,266.59C620.161,329.64 554.186,345.158 479.688,359.755C410.222,373.365 335.333,374.043 264.036,383.09C196.257,391.69 106.617,392.884 47.731,432.515C-9,470.695 -5.712,558.467 11.716,614.497C37.436,697.184 131.208,741.512 205.229,770.875C292.115,805.343 394.395,796.242 484.673,815.74C543.447,828.437 632.274,846.066 669.588,896.13C714.289,956.105 720.275,1015.489 674.616,1076.971C632.111,1134.209 552.072,1152.196 485.01,1169.825C391.941,1194.292 171.09,1138.987 135.326,1268.846C118.85,1328.652 191.327,1381.43 231.544,1409.869C289.505,1450.857 340.832,1469.519 407.393,1487.867C506.477,1515.179 676.052,1568.484 668.852,1697.888C664.962,1767.785 640.197,1814.969 565.581,1829.97C483.849,1846.403 399.726,1847.903 313.914,1847.903C272.363,1847.903 111.43,1863.815 72.315,1887.142C44.71,1903.604 21.879,1925.722 8.64,1955.972C-1.922,1980.108 5.601,2007.223 -3.215,2030.921"/>
</svg>
I am on my learning curve for Javascript/SVG combo (animating and making interactive SVGs).
I wanted to create a code snippet where menu elements ("inventory") can be dragged over to the main screen ("canvas") while the originating element would remain in its place (as if one would move a copy of it off the original element).
Here I crafted the code snippet as best as I could:
http://codepen.io/cmer41k/pen/f2b5eea274cdde29b0b2dc8a2424a645
So I sort of managed to do something but its buggy:
I could deal with 1 copy and making it draggable, but then I don't know how to deal with IDs for all those spawning elements, which causes dragging issues
I fail to understand how to make it work indefinitely (so that it can spawn any amount of circles draggable to canvas)
Draggable elements in canvas often overlap and I fail to attach the listeners the way they don't overlap so that the listener on the element I am dragging would propagate "through" whatever other elements there;(
Question is basically - can someone suggest logic that I should put into this snippet so that it was not as cumbersome. I am pretty sure I am missing something here;( (e.g. it should not be that hard is it not?)
HTML:
<body>
<svg id="svg"
height="800"
width="480"
viewbox="0 0 480 800"
preserveAspectRatio="xMinYMin meet"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<rect id="canvasBackground" width="480" height="480" x="0" y="0"/>
<rect id="inventoryBackground" width="480" height="100" x="0" y="480"/>
<g id="inventory">
<path id="curve4" class="inventory" d="M60,530 A35,35 0 1,1 60,531" />
<path id="curve3" class="inventory" d="M150,530 A35,35 0 1,1 150,531" />
<path id="curve2" class="inventory" d="M240,530 A35,35 0 1,1 240,531" />
<path id="curve1" class="inventory" d="M330,530 A35,35 0 1,1 330,531" />
</g>
<g id="canvas">
</g>
</svg>
</body>
Javascript:
// define meta objects
var drag = null;
// this stores all "curves"-circles
var curves = {};
var canvas = {};
var inventory = {};
window.onload = function() {
// creates the curve-circles in the object and at their initial x,y coords
curves.curve1 = document.getElementById("curve1");
curves.curve1.x = 0;
curves.curve1.y = 0;
curves.curve2 = document.getElementById("curve2");
curves.curve2.x = 0;
curves.curve2.y = 0;
curves.curve3 = document.getElementById("curve3");
curves.curve3.x = 0;
curves.curve3.y = 0;
curves.curve4 = document.getElementById("curve4");
curves.curve4.x = 0;
curves.curve4.y = 0;
canvas = document.getElementById("canvas");
inventory = document.getElementById("inventory");
// attach events listeners
AttachListeners();
}
function AttachListeners() {
var ttt = document.getElementsByClassName('inventory'), i;
for (i = 0; i < ttt.length; i++) {
document.getElementsByClassName("inventory")[i].onmousedown=Drag;
document.getElementsByClassName("inventory")[i].onmousemove=Drag;
document.getElementsByClassName("inventory")[i].onmouseup=Drag;
}
}
// Drag function that needs to be modified;//
function Drag(e) {
e.stopPropagation();
var t = e.target, id = t.id, et = e.type; m = MousePos(e);
if (!drag && (et == "mousedown")) {
if (t.className.baseVal=="inventory") { //if its inventory class item, this should get cloned into draggable?
copy = t.cloneNode(true);
copy.onmousedown=copy.onmousemove=onmouseup=Drag;
inventory.insertBefore(copy, inventory.firstChild);
drag = t;
dPoint = m;
}
if (t.className.baseVal=="draggable") { //if its just draggable class - it can be dragged around
drag = t;
dPoint = m;
}
}
// drag the spawned/copied draggable element now
if (drag && (et == "mousemove")) {
curves[id].x += m.x - dPoint.x;
curves[id].y += m.y - dPoint.y;
dPoint = m;
curves[id].setAttribute("transform", "translate(" +curves[id].x+","+curves[id].y+")");
}
// stop drag
if (drag && (et == "mouseup")) {
t.className.baseVal="draggable";
drag = null;
}
}
// adjust mouse position to the matrix of SVG & screen size
function MousePos(event) {
var p = svg.createSVGPoint();
p.x = event.clientX;
p.y = event.clientY;
var matrix = svg.getScreenCTM();
p = p.matrixTransform(matrix.inverse());
return {
x: p.x,
y: p.y
}
}
You were close. You had a couple of bugs. Eg.
copy.onmousedown=copy.onmousemove=onmouseup=Drag;
should have been:
copy.onmousedown=copy.onmousemove=copy.onmouseup=Drag;
And drag = t should have been drag = copy (?)
Also you were appending the clones to the inventory section, when I think you intended to append them to the "canvas" section.
But there were also also some less-obvious mistakes that were contributing to the unreliableness. For example, if you attach the mousemove and mouseup events to the inventory and clone shapes, then you will won't get the events if you drag too fast. The mouse will get outside the shape, and the events won't be passed to the shapes. The fix is to move those event handlers to the root SVG.
Another change I made was to store the x and y positions in the DOM for the clone as _x and _y. This makes it easier than trying to keep them in a separate array.
Anyway, here's my modified version of your example which works a lot more reliably.
// define meta objects
var drag = null;
var canvas = {};
var inventory = {};
window.onload = function() {
canvas = document.getElementById("canvas");
inventory = document.getElementById("inventory");
// attach events listeners
AttachListeners();
}
function AttachListeners() {
var ttt = document.getElementsByClassName('inventory'), i;
for (i = 0; i < ttt.length; i++) {
document.getElementsByClassName("inventory")[i].onmousedown=Drag;
}
document.getElementById("svg").onmousemove=Drag;
document.getElementById("svg").onmouseup=Drag;
}
// Drag function that needs to be modified;//
function Drag(e) {
var t = e.target, id = t.id, et = e.type; m = MousePos(e);
if (!drag && (et == "mousedown")) {
if (t.className.baseVal=="inventory") { //if its inventory class item, this should get cloned into draggable?
copy = t.cloneNode(true);
copy.onmousedown = Drag;
copy.removeAttribute("id");
copy._x = 0;
copy._y = 0;
canvas.appendChild(copy);
drag = copy;
dPoint = m;
}
else if (t.className.baseVal=="draggable") { //if its just draggable class - it can be dragged around
drag = t;
dPoint = m;
}
}
// drag the spawned/copied draggable element now
if (drag && (et == "mousemove")) {
drag._x += m.x - dPoint.x;
drag._y += m.y - dPoint.y;
dPoint = m;
drag.setAttribute("transform", "translate(" +drag._x+","+drag._y+")");
}
// stop drag
if (drag && (et == "mouseup")) {
drag.className.baseVal="draggable";
drag = null;
}
}
// adjust mouse position to the matrix of SVG & screen size
function MousePos(event) {
var p = svg.createSVGPoint();
p.x = event.clientX;
p.y = event.clientY;
var matrix = svg.getScreenCTM();
p = p.matrixTransform(matrix.inverse());
return {
x: p.x,
y: p.y
}
}
/* SVG styles */
path
{
stroke-width: 4;
stroke: #000;
stroke-linecap: round;
}
path.fill
{
fill: #3ff;
}
html, body {
margin: 0;
padding: 0;
border: 0;
overflow:hidden;
background-color: #fff;
}
body {
-ms-touch-action: none;
}
#canvasBackground {
fill: lightgrey;
}
#inventoryBackground {
fill: grey;
}
.inventory {
fill: red;
}
.draggable {
fill: green;
}
svg {
position: fixed;
top:0%;
left:0%;
width:100%;
height:100%;
}
<svg id="svg"
height="800"
width="480"
viewbox="0 0 480 800"
preserveAspectRatio="xMinYMin meet"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
>
<rect id="canvasBackground" width="480" height="480" x="0" y="0"/>
<rect id="inventoryBackground" width="480" height="100" x="0" y="480"/>
<g id="inventory">
<path id="curve4" class="inventory" d="M60,530 A35,35 0 1,1 60,531" />
<path id="curve3" class="inventory" d="M150,530 A35,35 0 1,1 150,531" />
<path id="curve2" class="inventory" d="M240,530 A35,35 0 1,1 240,531" />
<path id="curve1" class="inventory" d="M330,530 A35,35 0 1,1 330,531" />
</g>
<g id="canvas">
</g>
</svg>
How can I animate multiple paths at the same time using an svg and javascript.
Here is the javascript I am using:
var path = document.querySelector('#Layer_1 path');
var length = path.getTotalLength();
// Clear any previous transition
path.style.transition = path.style.WebkitTransition = 'none';
// Set up the starting positions
path.style.strokeDasharray = length + ' ' + length;
path.style.strokeDashoffset = length;
// Trigger a layout so styles are calculated & the browser
// picks up the starting position before animating
path.getBoundingClientRect();
// Define our transition
path.style.transition = path.style.WebkitTransition = 'stroke-dashoffset 2s ease-in-out';
// Go!
path.style.strokeDashoffset = '0';
Link to jsfiddle
The problem is that you are only getting one path with document.querySelector()
So if we change that to document.querySelectorAll() and iterate over it for all paths it works.
Like this:
var paths = document.querySelectorAll('#Layer_1 path');
[].forEach.call(paths, function(path) {
var length = path.getTotalLength();
// Clear any previous transition
path.style.transition = path.style.WebkitTransition =
'none';
// Set up the starting positions
path.style.strokeDasharray = length + ' ' + length;
path.style.strokeDashoffset = length;
// Trigger a layout so styles are calculated & the browser
// picks up the starting position before animating
path.getBoundingClientRect();
// Define our transition
path.style.transition = path.style.WebkitTransition =
'stroke-dashoffset 2s ease-in-out';
// Go!
path.style.strokeDashoffset = '0';
})
<body>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="196.039px" height="185.064px" viewBox="0 0 196.039 185.064" enable-background="new 0 0 196.039 185.064" xml:space="preserve">
<g>
<g>
<path fill="none" stroke="#651D79" stroke-width="3.7953" stroke-miterlimit="10" d="M91.93,59.704
c3.347-5.793,3.347-15.27,0-21.063l-16.5-28.594c-3.347-5.79-10.823-7.791-16.616-4.448l-16.493,9.525
c-5.789,3.347-7.791,10.815-4.447,16.615l16.504,28.576c3.343,5.79,0.604,10.534-6.078,10.534H15.298
c-6.69,0-12.161,5.478-12.161,12.157v19.043c0,6.69,5.471,12.164,12.161,12.164h33.004c6.687,0,14.896-4.744,18.239-10.533
L91.93,59.704z" />
</g>
<g>
<g>
<path fill="none" stroke="#651D79" stroke-width="3.7953" stroke-miterlimit="10" d="M72.631,114.213
c-6.69,0-14.899,4.737-18.247,10.526l-16.508,28.594c-3.343,5.793-1.342,13.269,4.455,16.616l16.486,9.514
c5.796,3.347,13.269,1.345,16.615-4.448l16.5-28.583c3.347-5.8,8.817-5.8,12.165,0l16.497,28.576
c3.343,5.789,10.822,7.791,16.612,4.448l16.489-9.518c5.793-3.343,7.798-10.822,4.451-16.612l-16.5-28.576
c-3.347-5.8-11.556-10.537-18.239-10.537H72.631z" />
</g>
<g>
<path fill="none" stroke="#651D79" stroke-width="3.7953" stroke-miterlimit="10" d="M129.479,103.68
c3.347,5.789,11.556,10.526,18.246,10.526h33.012c6.69,0,12.164-5.467,12.164-12.157V83.006
c-0.007-6.69-5.478-12.164-12.168-12.164l-33.005,0.007c-6.686,0-9.421-4.744-6.078-10.534l16.497-28.576
c3.347-5.8,1.346-13.269-4.451-16.615l-16.489-9.525c-5.79-3.343-13.269-1.334-16.608,4.459l-16.5,28.576
c-3.347,5.789-3.347,15.27,0,21.07L129.479,103.68z" />
</g>
</g>
</g>
</svg>
</body>
You can loop over all paths
var paths = document.querySelectorAll('#Layer_1 path');
for (i = 0; i < paths.length; i++) {
var length = paths[i].getTotalLength();
// Clear any previous transition
paths[i].style.transition = paths[i].style.WebkitTransition ='none';
// Set up the starting positions
paths[i].style.strokeDasharray = length + ' ' + length;
paths[i].style.strokeDashoffset = length;
// Trigger a layout so styles are calculated & the browser
// picks up the starting position before animating
paths[i].getBoundingClientRect();
// Define our transition
paths[i].style.transition = paths[i].style.WebkitTransition = 'stroke-dashoffset 3s ease-in-out';
// Go!
paths[i].style.strokeDashoffset = '0';
}
JSFiddle
Notice: You can try also Vivus library, is light and easy to maintain
Is there any way i can drag an SVG element over another SVG element? I tried but like in this tutorial i can only drag the one i placed the second over the first one. There is no way i can drag first one over the second without problems.
Does anyone know how to solve this?
Here is the whole tutorial: http://www.petercollingridge.co.uk/book/export/html/437
I was writing it before I saw that #Strat-O gave you the same approach.
So here is a commented example of that :
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" version="1.1" width="400" height="200">
<style>
.draggable {
cursor: move;
}
</style>
<script type="text/ecmascript"><![CDATA[
var selectedElement = 0;
var currentX = 0;
var currentY = 0;
var currentMatrix = 0;
function cloneToTop(oldEl){
// already at top, don't go farther…
if(oldEl.atTop==true) return oldEl;
// make a copy of this node
var el = oldEl.cloneNode(true);
// select all draggable elements, none of them are at top anymore
var dragEls= oldEl.ownerDocument.documentElement.querySelectorAll('.draggable');
for(i=0; i<dragEls.length; i++){
dragEls[i].atTop=null;
}
var parent = oldEl.parentNode;
// remove the original node
parent.removeChild(oldEl);
// insert our new node at top (last element drawn is first visible in svg)
parent.appendChild(el);
// Tell the world that our new element is at Top
el.atTop= true;
return el;
}
function selectElement(evt) {
selectedElement = cloneToTop(evt.target);
currentX = evt.clientX;
currentY = evt.clientY;
currentMatrix = selectedElement.getAttributeNS(null, "transform").slice(7,-1).split(' ');
for(var i=0; i<currentMatrix.length; i++) {
currentMatrix[i] = parseFloat(currentMatrix[i]);
}
selectedElement.setAttributeNS(null, "onmousemove", "moveElement(evt)");
selectedElement.setAttributeNS(null, "onmouseout", "deselectElement(evt)");
selectedElement.setAttributeNS(null, "onmouseup", "deselectElement(evt)");
}
function moveElement(evt) {
var dx = evt.clientX - currentX;
var dy = evt.clientY - currentY;
currentMatrix[4] += dx;
currentMatrix[5] += dy;
selectedElement.setAttributeNS(null, "transform", "matrix(" + currentMatrix.join(' ') + ")");
currentX = evt.clientX;
currentY = evt.clientY;
}
function deselectElement(evt) {
if(selectedElement != 0){
selectedElement.removeAttributeNS(null, "onmousemove");
selectedElement.removeAttributeNS(null, "onmouseout");
selectedElement.removeAttributeNS(null, "onmouseup");
selectedElement = 0;
}
}
]]> </script>
<g>
<circle/>
</g>
<rect x="0.5" y="0.5" width="399" height="199" fill="none" stroke="black"/>
<rect class="draggable" id="blue" x="30" y="30" width="80" height="80" fill="blue" transform="matrix(1 0 0 1 46 18)" onmousedown="selectElement(evt)"/>
<rect class="draggable" id="green" x="160" y="50" width="50" height="50" fill="green" transform="matrix(1 0 0 1 51 11)" onmousedown="selectElement(evt)"/>
</svg>
Unfortunately, there is only one way to make an element appear in front of another element in SVG and that is to remove the lower element then turn around and redraw it. There is no z-index or other helpful attribute that you can set. I spent a bit of time on this and that is my conclusion.
There is one upside and that is by removing and redrawing it, it ensures that the display order of all of the elements is maintained whereas if you have to maintain z-indexes, managing the numbers can cause its own set of issues.