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/

Shanon Jackson
  • 5,873
  • 1
  • 19
  • 39

3 Answers3

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>
Jon Uleis
  • 17,693
  • 2
  • 33
  • 42
  • 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 – Shanon Jackson Sep 26 '17 at 23:25
  • @ShanonJackson it does work see my [Fiddle](https://jsfiddle.net/zer00ne/zxksccxL/1/) – zer00ne Sep 27 '17 at 00:26
  • @JonUleis why would you say that `transform:translate` doesn't work on SVG? – zer00ne Sep 27 '17 at 00:28
  • @zer00ne I hadn't realized how the SVG was hidden and implemented with a `` here. Thanks for pointing that out. I've removed my incorrect assumptions, which were based on OP's original JSFiddle. – Jon Uleis Sep 27 '17 at 00:55
  • check out my new solution, added at bottom. thanks for help guys – Shanon Jackson Sep 27 '17 at 02:09
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>
zer00ne
  • 41,936
  • 6
  • 41
  • 68
  • 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 but .... if you take off display none you can transition paths in – Shanon Jackson Sep 27 '17 at 20:12
  • @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](https://codepen.io/AmeliaBR/pen/lshrp), it's very enlightening and I'm sure you'll be very interested. Don't forget to accept this answer if it's right. – zer00ne Sep 27 '17 at 20:27
0

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>
Shanon Jackson
  • 5,873
  • 1
  • 19
  • 39