2

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/

6
  • 2
    ...maybe 0.4s? Commented Sep 26, 2017 at 23:18
  • added fiddlr to prove this is not the issue. sorry for delay Commented Sep 26, 2017 at 23:26
  • Just tested fiddle and the text is supposed to move correct? Slow your transition down to 1s and you'll see it work. Commented Sep 26, 2017 at 23:29
  • @JonUleis Remove the display:none from <div style='display:none'> that SVG moves just fine. Commented Sep 27, 2017 at 0:03
  • Sorry i meant through the "Use" tag, target a individual path, translateY Commented Sep 27, 2017 at 2:11

3 Answers 3

5

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>

Sign up to request clarification or add additional context in comments.

5 Comments

added fiddlr to prove this is not the issue, sorry that was a typo. Its an issue with SVG use elements and javascript and its been making me pull my hair out for hours now
@ShanonJackson it does work see my Fiddle
@JonUleis why would you say that transform:translate doesn't work on SVG?
@zer00ne I hadn't realized how the SVG was hidden and implemented with a <use> here. Thanks for pointing that out. I've removed my incorrect assumptions, which were based on OP's original JSFiddle.
check out my new solution, added at bottom. thanks for help guys
2

Update

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:

    • Removed the 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


Old

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>

Demo 1 (not functioning see Fiddle w/o <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>

Demo 2

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>

Demo 3 (not functioning see Fiddle 3 with <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>

2 Comments

Demo 3 is exactly what i want thanks. This has been the wierdest problem to solve. Document.getElementById... or getElementsByClassName can't target within the use tag, but querySelector can. Display:None on sprite sheet stops transitions on paths in <use> but .... if you take off display none you can transition paths in <use>
@ShanonJackson Yes SVG is a strange beast indeed. I believe the reason why qS() can access SVG is because it uses CSS/jQuery selector type while the getElement* methods use a normal string. Using a normal string involves more interaction with the DOM which SVG is only a visitor of the DOM not entirely accessible. Take a look at this Codepen, it's very enlightening and I'm sure you'll be very interested. Don't forget to accept this answer if it's right.
1

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.