Shanon Jackson
Shanon Jackson

Reputation: 6531

Javascript Animation Issues - Transition not working

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

Answers (3)

zer00ne
zer00ne

Reputation: 43853

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>

Upvotes: 2

Shanon Jackson
Shanon Jackson

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

Jon Uleis
Jon Uleis

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

Related Questions