Reputation: 6531
I'm trying to animate a SVG on the page using only JavaScript (NO CSS). However, the transition isn't applying the delay
dot.style.transition = "all 0.4s ease";
dot.style.transform = "translateY(-5px)";
this results in its translateY being applied, but not transitioned. Why is this and how can i avoid it?
https://jsfiddle.net/0nmha9uf/ Svg seems completley bugged.
EDIT: fixed typo on 0.4s - this was not the issue.
EDIT 3: Solved, leveraging requestAnimationFrame https://jsfiddle.net/ke5fnp9h/3/
Upvotes: 2
Views: 204
Reputation: 43853
Support for transition
on the <use>
tag does exist, but it's buggy on Chrome. Demo 3 has OP code with 2 adjustments:
The <div style='display:none'>
that hides the original svg has been changed with the following:
style
attribute.added the following class:
.svg {
position:relative;
left:-999px;
}
The reason why display:none
doesn't work is because in Chrome the primary SVG needs a repaint to follow through and let the <use>
clone mimic it. display:none
removes the primary out of the document's flow. So by keeping the primary SVG in the DOM but out of sight, you can do the CSS magic on it and <use>
should play along nicely.
See Fiddle 3
OK, This issue is resolved and yes of course you can use transform:translate
on SVG. I have removed that <use>
and shrunk the real SVG to 48x48.
See demo 2 for a better way of animating SVG paths using setAttributeNS()
. If you still want to use <use>
the way you were trying to do (not recommended), you'll need to familiarize yourself with the wonderful world of namespaces.
See Fiddle 1 w/o <use>
and Fiddle 3 with <use>
This Stack Snippet does not function see Fiddle and Fiddle 3 with <use>
<use>
instead)var firstDot = document.querySelector("#icon-ellipsis > path:nth-child(2)")
firstDot.style.transition = "all 1s ease";
firstDot.style.transform = "translateY(-5px)";
//why does it not slide into translateY(-5px;)
var div = document.getElementById("div");
div.style.transition = "all 1s ease";
div.style.transform = "translateY(10px)";
//div works fine, slides into place.
#wrapper {
height: 48px;
width: 48px;
}
#div {
width: 50px;
height: 50px;
background-color: lightcoral;
}
<body>
<div>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" width="48" height="48">
<svg id="icon-ellipsis" class="icon-ellipsis" width="100%" height="100%" viewBox="0 0 24 24">
<path class="icon-ellipsis-dot" d="M6 11.59c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2s2 0.895 2 2z"></path>
<path class="icon-ellipsis-dot" d="M14 11.59c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2s2 0.895 2 2z"></path>
<path class="icon-ellipsis-dot" d="M22 11.59c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2s2 0.895 2 2z"></path>
</svg>
</svg>
</div>
<!-- above is the sprite sheet -->
<div id="div">
Testing Div not svg.
</div>
</body>
var firstDot = document.querySelector("#icon-ellipsis > path:nth-child(2)");
var A = document.querySelector('#anim');
A.setAttributeNS(null, "dur",".4s");
A.setAttributeNS(null, "path","M 0 0 L 0 -5");
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g transform="translate(0,20)">
<svg id="icon-ellipsis" class="icon-ellipsis" width="48" height="48" viewBox="0 0 36 36">
<path class="icon-ellipsis-dot" d="M6 11.59c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2s2 0.895 2 2z"></path>
<path class="icon-ellipsis-dot" d="M14 11.59c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2s2 0.895 2 2z">
<animateMotion id='anim' fill="freeze" />
</path>
<path class="icon-ellipsis-dot" d="M22 11.59c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2s2 0.895 2 2z"></path>
</svg>
</g>
</svg>
<use>
instead)var firstDot = document.querySelector("#icon-ellipsis > path:nth-child(2)")
firstDot.style.transition = "all 0.4s ease";
firstDot.style.transform = "translateY(-5px)";
//why does it not slide into translateY(-5px;)
var div = document.getElementById("div");
div.style.transition = "all 0.4s ease";
div.style.transform = "translateY(10px)";
//div works fine, slides into place.
#wrapper {
height: 48px;
width: 48px;
}
#div {
width: 50px
height: 50px
background-color: lightcoral;
}
.svg {
position:relative;
left:-999px;
}
<body>
<div class='svg'>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" width="48" height="48">
<svg id="icon-ellipsis" class="icon-ellipsis" width="100%" height="100%" viewBox="0 0 24 24">
<path class="icon-ellipsis-dot" d="M6 11.59c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2s2 0.895 2 2z"></path>
<path class="icon-ellipsis-dot" d="M14 11.59c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2s2 0.895 2 2z"></path>
<path class="icon-ellipsis-dot" d="M22 11.59c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2s2 0.895 2 2z"></path>
</svg>
</svg>
</div>
<!-- above is the sprite sheet -->
<div id="wrapper">
<svg style="height: 100%;width:100%">
<use xlink:href="#icon-ellipsis"></use>
</svg>
</div>
<div id="div">
Testing Div not svg.
</div>
</body>
Upvotes: 2
Reputation: 6531
FIXED
Thanks for all you're answers guys, i appreciate the feedback. Now that i learnt transition isn't a viable solution i solved my problem using this.
https://jsfiddle.net/ke5fnp9h/3/
jiggle();
If anyone has a solution that can use a javascript function to jiggle the dots like that (mine will soon be running 'OnMouseEnter') with less logic than that it would be greatly appreciated.
Thanks so much, and definitely check out my Fiddlr where i leverage "requestAnimationFrame" and loop it on itself, where with every loop i change its static translateY by a tiny ammount.
const jiggle = () => {
const dots = (document.querySelectorAll("#icon-ellipsis > path.icon-ellipsis-dot"))
let delay = 100;
[].forEach.call(dots, (dot, i) => {
jsRequestAnimationFrame(delay, dot, () => {
jsRequestAnimationFrame(delay, dot, () => {
return;
}, true);
}, false);
delay = delay + 75;
});
}
const jsRequestAnimationFrame = (timeout, element, cb, mode) => {
let firstLoad = true;
let newTimeout;
const tick = (time) => {
if(firstLoad) {
newTimeout = timeout + time;
firstLoad = false;
}
// if time >= newTimeout we're done looping, time to run the callback and leave.
if(time >= newTimeout) return cb();
const calculatePixels = () => {
if(mode) return 4 - (4 * (1 - (newTimeout - time) / timeout));
return 4 * (1 - (newTimeout - time) / timeout);
};
element.style.transform = `translateY(-${(calculatePixels())}px)`;
requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
};
document.getElementById("btn").addEventListener("click", jiggle)
jiggle();
#wrapper {
height: 48px;
width: 48px;
}
#div {
width: 50px
height: 50px
background-color: lightcoral;
}
<body>
<div style="display:none">
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" width="384" height="304">
<svg id="icon-ellipsis" class="icon-ellipsis" width="100%" height="100%" viewBox="0 0 24 24">
<path class="icon-ellipsis-dot" d="M6 11.59c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2s2 0.895 2 2z"></path>
<path class="icon-ellipsis-dot" d="M14 11.59c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2s2 0.895 2 2z"></path>
<path class="icon-ellipsis-dot" d="M22 11.59c0 1.105-0.895 2-2 2s-2-0.895-2-2c0-1.105 0.895-2 2-2s2 0.895 2 2z"></path>
</svg>
</svg>
</div>
<!-- above is the sprite sheet -->
<div id="wrapper">
<svg style="height: 100%;width:100%">
<use xlink:href="#icon-ellipsis"></use>
</svg>
</div>
<button id="btn">Jiggle :)</button>
</body>
Upvotes: 1
Reputation: 18639
You're missing the unit on your 0.4
. Should be 0.4s
.
Here's a working example. Click the dot:
var dot = document.getElementById('dot');
dot.addEventListener('click', function() {
dot.style.transition = "all 0.4s ease";
dot.style.transform = "translateY(-5px)";
});
#dot {
background: #000;
border-radius: 50%;
height: 1em;
width: 1em;
}
<div id="dot"></div>
Upvotes: 5