0

I'm facing a "problem" with CSS animations.

I have an SVG that is composed of multiple items that represent a face, eyes,eyebrows, hands and a notebook.

As you can imagine I am trying to animate those part of the SVG. I have 3 buttons that represent 3 type of animations. Those are CSS Keyframes that are added to the elements that need to be animated.

My problem : I am unable to run the same animation twice in a row while I am able to launch different types of animations one by one.

I found that "trick" that was supposed to "reset" the animations but that does not seem to work :

        currItem.style.animation = 'none';
        currItem.offsetHeight;
        currItem.style.animation = null;

CSS animations are kind of new for me so I could miss some essential "requirements".

PS: I know my code is a bit ugly but I'm in a "sandbox" mode atm and am not trying to optimize my code.

Thanks.

Flo
  • 936
  • 1
  • 8
  • 19
  • 1
    My problem: I can't see the code in the post ..? – Teemu Jul 19 '18 at 13:03
  • Please go read [ask] and [mcve]. Code relevant to your problem belongs directly into your question to begin with, not just dumped on external sites. Please edit your question accordingly. – CBroe Jul 19 '18 at 13:05
  • possible duplicate of : https://stackoverflow.com/questions/6268508/restart-animation-in-css3-any-better-way-than-removing-the-element – Temani Afif Jul 19 '18 at 13:15
  • Believe me I've tried many solutions before posting it, including the post you attached.. – Flo Jul 19 '18 at 13:18
  • it works fine here .. I guess you found a JSFiddle bug – Temani Afif Jul 19 '18 at 13:20
  • no no it's not related to JSFiddle as it does the same on my local machine :) – Flo Jul 19 '18 at 13:23

1 Answers1

1

I don't know why you can't perform the animation twice in a row, but I found a workaround : wrap do_animation inside a setTimeout. This forces do_animation to execute at the end of the execution stack, so I guess the buggy behaviour comes from the style not being applied in real time in the CSS engine or something like that.

Also, I believe Promises here are completely useless, because everything you do is synchronous.

EDIT : I have also optimized the code, cached all elements and used ES6. Sorry, couldn't help doing that :)

var arr_items_to_animate = [
    'left_eyebrow',
    'right_eyebrow',
    'face',
    'hands_filled',
    'right_eye',
    'eyes',
    'notebook',
    'eyebrows',
    'right_hand'
  ].map(id => document.getElementById(id)),
  RightEye = document.getElementById('right_eye'),
  Eyes = document.getElementById('eyes'),
  Face = document.getElementById('face');

// reinit + start animation
const animate_bot = animationType => {
  reInit();
  setTimeout(() => {
    do_animation(animationType);
  })
}

// makes the animation
const do_animation = animationType => {
  console.log("do_animation(" + animationType + ")");

  switch (animationType) {
    case 'wink':
      RightEye.style.animation = "wink 0.3s ease-in 0s 1";
      break;
    case 'blink':
      Eyes.style.animation = "blink 0.5s ease-in 0s 1";
      break;
    case 'nope':
      Face.style.animation = "nope 1s ease-in 0s 1";
      break;
  }
}

// reinit all parts of svg
const reInit = () => {
  // resets every part of svg
  arr_items_to_animate.forEach(Elem => {
    Elem.style.animation = 'none';
    Elem.style.animation = null;
  });
}
@keyframes wink {
  0% {
    transform: translate(0px, 0px)
  }
  30% {
    transform: scale(1, 0.1) translate(0px, 300px)
  }
  70% {
    transform: scale(1, 0.1) translate(0px, 300px)
  }
  100% {
    transform: translate(0px, 0px)
  }
}

@keyframes blink {
  0% {
    transform: translate(0px, 0px)
  }
  25% {
    transform: scale(1, 0.1) translate(0px, 300px)
  }
  50% {
    transform: translate(0px, 0px)
  }
  75% {
    transform: scale(1, 0.1) translate(0px, 300px)
  }
  100% {
    transform: translate(0px, 0px)
  }
}

@keyframes nope {
  0% {
    transform: translate(0px, 0px)
  }
  40% {
    transform: translate(0px, 0px)
  }
  50% {
    transform: translate(2px, 0px)
  }
  60% {
    transform: translate(-2px, 0px)
  }
  70% {
    transform: translate(2px, 0px)
  }
  80% {
    transform: translate(-2px, 0px)
  }
  90% {
    transform: translate(2px, 0px)
  }
  100% {
    transform: translate(0px, 0px)
  }
}
<button onclick="animate_bot('nope')">Nope</button>
<button onclick="animate_bot('blink')">Blink</button>
<button onclick="animate_bot('wink')">Wink</button>

<svg id="myBot" viewBox="0 0 300 500">
    <path id="head" d="M24,55A25,25 0 1,1 46,55" stroke="#1E569F" fill="none" stroke-width="9" />
    <g id="face">
      <g id="eyes">
        <circle id="left_eye" cx="27" cy="32" r="3" stroke="none" fill="black" />
        <circle id="right_eye" cx="42" cy="32" r="3" stroke="none" fill="black" />

      </g>
      <g id="eyebrows">
        <path id="left_eyebrow" d="M22 27 Q 27 24 30 27" stroke="black" stroke-width="2" fill="none" />
        <path id="right_eyebrow" d="M39 27 Q 42 24 47 27" stroke="black" stroke-width="2" fill="none" />
      </g>
    </g>

    <g id="hands_filled"></g>
    <g id="notebook">
      <rect id="page" x="24" y="58" width="22" height="30" stroke="none" fill="#ee9220" />
      <g id="text">
        <line id="line1" x1="26" y1="63" x2="44" y2="63" stroke="white" />
        <line id="line2" x1="26" y1="67" x2="44" y2="67" stroke="white" />
        <line id="line3" x1="26" y1="71" x2="44" y2="71" stroke="white" />
      </g>
    </g>
    <ellipse id="right_hand" cx="46" cy="76" rx="4" ry="5" stroke="none" fill="black" />
    <ellipse id="left_hand" cx="24" cy="80" rx="4" ry="5" stroke="none" fill="black" />
    Sorry, your browser does not support inline SVG.
  </svg>
Jeremy Thille
  • 26,047
  • 12
  • 43
  • 63