SVG Animate Opacity on Variable Value - javascript

I am trying to animate an svg element to disappear (slowly and gracefully) when a binary variable is set to zero and then reappear (again, nice and slow) when the binary variable is back to one. I have the image disappearing, but cant seem to figure out haw to get it to reappear. Here is the JavaScript:
function go() {
var max = 2;
var random = Math.floor(Math.random() * Math.floor(max));;
console.log("hi" + random);
var elements = document.getElementsByTagName("animate");
for (var i = 0; i < elements.length; i++) {
if (random == 1) {
elements[i].beginElement();
}
else {
elements[i].endElement();
}
}
}
and here is the SVG elements
<svg xmlns="http://www.w3.org/2000/svg" class="icons" width="40" height="40">
<g class="icon" transform="translate(-2 -2)">
<path class="icon__sight" d="M38.255 20.182A16.353 16.353 0 0 0 23.818 5.745V2h-3.636v3.745A16.353 16.353 0 0 0 5.745 20.182H2v3.636h3.745a16.353 16.353 0 0 0 14.437 14.437V42h3.636v-3.745a16.353 16.353 0 0 0 14.437-14.437H42v-3.636h-3.745zM22 34.727A12.727 12.727 0 1 1 34.727 22 12.718 12.718 0 0 1 22 34.727z" fill="#fff"/>
<circle class="icon__indicator" cx="8" cy="8" r="8" transform="translate(14 14)" fill="#88db29">
<animate dur="1" attributeName="opacity" from="1" to="0" begin="move.end" repeatCount="0" fill="freeze" />
</circle>/>
</g>
Any tips or advice is greatly appreciated. Be kind, I am new to this :)

This seems overly complicated for simply transitioning the opacity of the SVG. Why not utilize CSS to transition the state with manipulating the class via your JS?
svg {
transition: opacity 0.5s ease;
}
svg.hidden {
opacity: 0;
}
var svg = document.querySelector('svg');
...
if (random == 1) {
svg.classList.remove('hidden');
}
else {
svg.classList.add('hidden');
}

Related

SVG animation triggering on load rather than on DOM insertion

The code below animates a SVG circle changing color and works as expected.
If the call to SVG.addAnimatedCircle(this.root) is made from within the callback method (instead of where it is below, inside the constructor), the animation starts when the document is loaded — and is therefore invisible unless the window is clicked — rather than when the event is triggered.
class SVG {
constructor() {
const root = document.createElementNS(
'http://www.w3.org/2000/svg', 'svg');
root.setAttribute('viewBox', '-50 -50 100 100');
this.root = root;
this.callback = this.callback.bind(this);
window.addEventListener('click', this.callback);
SVG.addAnimatedCircle(this.root);
}
callback() {
// SVG.addAnimatedCircle(this.root);
}
static addAnimatedCircle(toElement) {
const el = document.createElementNS(
'http://www.w3.org/2000/svg', 'circle');
el.setAttribute('cx', '0');
el.setAttribute('cy', '0');
el.setAttribute('r', '10');
el.setAttribute('fill', 'red');
toElement.appendChild(el);
const anim = document.createElementNS(
'http://www.w3.org/2000/svg', 'animate');
anim.setAttribute('attributeName', 'fill');
anim.setAttribute('from', 'blue');
anim.setAttribute('to', 'red');
anim.setAttribute('dur', '3s');
el.appendChild(anim);
}
}
const svg = new SVG();
document.body.appendChild(svg.root);
(The above doesn't need to be inside a class of course, I'm simplifying a more complex class).
Why is that? Isn't the animation supposed to start when the element is created and added to the DOM?
The <animate> element you create will have its begin attribute computed to 0s (since unset).
This 0s value is relative to the "document begin time", which itself in this HTML document corresponds to the root <svg>'s current time.
This means that if you do create such an <animate> element after its root <svg> element has been in the DOM, its animation state will depend on how long the root <svg> element has been in the DOM:
const root = document.querySelector("svg");
const circles = document.querySelectorAll("circle");
const duration = 3;
// will fully animate
circles[0].append(makeAnimate());
// will produce only half of the animation
setTimeout(() => {
circles[1].append(makeAnimate());
}, duration * 500);
// will not animate
setTimeout(() => {
circles[2].append(makeAnimate());
}, duration * 1000);
function makeAnimate() {
const anim = document.createElementNS("http://www.w3.org/2000/svg", "animate");
anim.setAttribute("attributeName", "fill");
anim.setAttribute("from", "blue");
anim.setAttribute("to", "red");
anim.setAttribute("fill", "freeze");
anim.setAttribute("dur", duration + "s");
return anim;
}
circle { fill: blue }
<svg height="60">
<circle cx="30" cy="30" r="25"/>
<circle cx="90" cy="30" r="25"/>
<circle cx="150" cy="30" r="25"/>
</svg>
<p>left circle starts immediately, and fully animates</p>
<p>middle circle starts after <code>duration / 2</code> and matches the same position as left circle</p>
<p>right circle starts after <code>duration</code>, the animation is already completed by then, nothing "animates"</p>
We can set the <svg>'s current time trough its SVGSVGElement.setCurrentTime() method.
So to create an <animate> that would start at the time it got created, no matter when it is, we could use this, however, this will also affect all the other <animate> that are already in the <svg>:
const root = document.querySelector("svg");
const circles = document.querySelectorAll("circle");
const duration = 3;
circles[0].append(makeAnimate());
root.setCurrentTime(0); // reset <animate> time
setTimeout(() => {
circles[1].append(makeAnimate());
root.setCurrentTime(0); // reset <animate> time
}, duration * 500);
setTimeout(() => {
circles[2].append(makeAnimate());
root.setCurrentTime(0); // reset <animate> time
}, duration * 1000);
function makeAnimate() {
const anim = document.createElementNS("http://www.w3.org/2000/svg", "animate");
anim.setAttribute("attributeName", "fill");
anim.setAttribute("from", "blue");
anim.setAttribute("to", "red");
anim.setAttribute("fill", "freeze");
anim.setAttribute("dur", duration + "s");
return anim;
}
circle { fill: blue }
<svg height="60">
<circle cx="30" cy="30" r="25"/>
<circle cx="90" cy="30" r="25"/>
<circle cx="150" cy="30" r="25"/>
</svg>
So, while it may do for some users, in most cases it's probably better to instead set only the <animate>'s begin attribute.
Luckily, we can also get the current time, with the SVGSVGElement.getCurrentTime() method.
const root = document.querySelector("svg");
const circles = document.querySelectorAll("circle");
const duration = 3;
circles[0].append(makeAnimate());
setTimeout(() => {
circles[1].append(makeAnimate());
}, duration * 500);
setTimeout(() => {
circles[2].append(makeAnimate());
}, duration * 1000);
function makeAnimate() {
const anim = document.createElementNS("http://www.w3.org/2000/svg", "animate");
anim.setAttribute("attributeName", "fill");
anim.setAttribute("from", "blue");
anim.setAttribute("to", "red");
anim.setAttribute("fill", "freeze");
anim.setAttribute("dur", duration + "s");
// set the `begin` to "now"
anim.setAttribute("begin", root.getCurrentTime() + "s");
return anim;
}
circle { fill: blue }
<svg height="60">
<circle cx="30" cy="30" r="25"/>
<circle cx="90" cy="30" r="25"/>
<circle cx="150" cy="30" r="25"/>
</svg>
But the way we usually do this is to use the API fully and control it all through JS, since you already did start using JS.
To do so, we set the begin attribute to "indefinite", so that it doesn't start automatically, and then we call the SVGAnimateElement (<animate>)'s beginElement() method, which will start the animation manually, when we want:
const root = document.querySelector("svg");
const circles = document.querySelectorAll("circle");
const duration = 3;
{
const animate = makeAnimate();
circles[0].appendChild(animate);
animate.beginElement();
}
setTimeout(() => {
const animate = makeAnimate();
circles[1].appendChild(animate);
animate.beginElement();
}, duration * 500);
setTimeout(() => {
const animate = makeAnimate();
circles[2].appendChild(animate);
animate.beginElement();
}, duration * 1000);
function makeAnimate() {
const anim = document.createElementNS("http://www.w3.org/2000/svg", "animate");
anim.setAttribute("attributeName", "fill");
anim.setAttribute("from", "blue");
anim.setAttribute("to", "red");
anim.setAttribute("fill", "freeze");
anim.setAttribute("dur", duration + "s");
// set the `begin` to "manual"
anim.setAttribute("begin", "indefinite");
return anim;
}
circle { fill: blue }
<svg height="60">
<circle cx="30" cy="30" r="25"/>
<circle cx="90" cy="30" r="25"/>
<circle cx="150" cy="30" r="25"/>
</svg>
(Not the answer, just presentation of the problem for possible accomodation into the question and/or proper answer suggested by OP in their comment.
<style>svg {outline: #0FF6 solid; outline-offset: -2px;}</style>
<table role="presentation" border><tr><td>
1. Static SVG with animated circle:
<td>
<svg viewBox="-50 -50 100 100" width="30" height="30">
<circle r="40" fill="black">
<animate begin="0.5s" fill="freeze" attributeName="fill" from="blue" to="red" dur="5s"></animate>
</circle>
</svg>
<tr><td>
2. Empty SVG,
<button onclick='
emptySVG.innerHTML = document.querySelector("circle").outerHTML;
'>Put similar animated circle into it</button>:
<td>
<svg id="emptySVG" viewBox="-50 -50 100 100" width="30" height="30">
<!-- empty -->
</svg>
<tr><td>
3. <button onclick='
sampleCell.innerHTML = document.querySelector("svg").outerHTML
'>Create new SVG with animated circle</button>:
<td id="sampleCell">
(here.)
</table>
<p>
<button onclick="location.reload()">Reload page</button>
<button onclick="
[...document.querySelectorAll('animate')].forEach(a=>{
//a.setAttribute('begin','indefinite'); // does not seem to be necessary
a.beginElement();
})
">Reset all animations</button>
Putting animated circle to second SVG produces state that corresponds with already elapsed duration in existing empty SVG: it matches the first one, so it either runs in sync or is finished. Goal is to run the animation upon circle's appearance.

3 sec animation that repeats with requestAnimationFrame

I have an SVG with an animation using requestAnimationFrame and the animation works but what I want it to do is animate over 3 secs and then repeat, so the animation for the circle will animate the dasharray and dashoffsset over 3seconds I don't know how to do this using css because it requires a calculation in javascript using Math.Pi can someone see if they can make this animation work the way I described. codepen.io
HTML
<svg width="20" height="20" viewBox="0 0 20 20">
<circle cx="10" cy="10" r="7" fill="none" stroke="#e6e6e6" stroke-width="6" />
<circle id="shape" cx="10" cy="10" r="7" fill="none" stroke="#ff0000" stroke-width="6" stroke-linecap="square" />
</svg>
CSS
#shape {
fill: none;
stroke: red;
stroke-width: 6;
transition: all 4s ease-in-out;
transform: rotate(-90deg);
transform-origin: 50%;
}
JavaScript
var endPercentage = 100;
var shape = document.getElementById('shape');
var circumference = Math.PI * parseFloat(shape.getAttribute('r')) * 2;
shape.setAttribute('stroke-dasharray', circumference);
shape.setAttribute('stroke-dashoffset', circumference);
var updatePercentage = function () {
var dashOffset = parseFloat(getComputedStyle(shape)['stroke-dashoffset']);
var curPercentage = Math.floor((100 * (circumference - dashOffset)) / circumference) || 0;
document.getElementById('perc').getElementsByClassName('value')[0].innerText = curPercentage;
if (curPercentage < endPercentage) {
window.requestAnimationFrame(updatePercentage);
}
}
var animateShape = function (percent) {
var offset = circumference - (circumference * percent / 100);
shape.setAttribute('stroke-dashoffset', offset);
window.requestAnimationFrame(updatePercentage);
}
setTimeout(function () {
animateShape(endPercentage);
}, 0);
You can define pi with:
:root {
--PI: 3.14159265358979;
}
...
stroke-dashoffset: calc(2 * var(--PI) * 7);
/* 7 is the radius of your circle */
With a combination of this and calc, you should be able to get a pure CSS solution.
If you still want to go via the JS route, I recommend:
shape.animate([
{ strokeDashoffset: 0 },
{ strokeDashoffset: circumference }
], { duration: 3000, iterations: Infinity });
Warning: polyfill required for older browsers.

spawn & drag of SVG elements - approach

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 animated multiple svg paths?

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

Dragging SVG element over another SVG element

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.

Categories

Resources