skip to Main Content

I want to create an animation in css and then link in to javascript,
like when pressing a button to run the css animation
lets say you have:
animation-name: vs-animation
and @keyframes with the same name
and i would like it to link to startNewRound() = true , how would i do that

2

Answers


  1. To create a CSS animation linked to JavaScript when clicking a button, you can achieve this by using JavaScript to add and remove CSS classes containing the animation. Here’s how to do it step by step:

    1. Define your CSS animation using @keyframes. For example:
    @keyframes vs-animation {
      0% {
        /* Initial state of the animation */
        transform: scaleX(1);
      }
      50% {
        /* Intermediate state of the animation */
        transform: scaleX(0.5);
      }
      100% {
        /* Final state of the animation */
        transform: scaleX(1);
      }
    }
    
    1. In your HTML file, create a button that will call the startNewRound() function when clicked:
    <button id="startButton">Start Animation</button>
    
    1. In your JavaScript file, add a click event to the button and use JavaScript to apply or remove the animation class based on the startNewRound() function:
    // Get a reference to the button and the element you want to animate
    const startButton = document.getElementById("startButton");
    const elementToAnimate = document.getElementById("elementToAnimate");
    
    // Add a click event to the button
    startButton.addEventListener("click", function() {
      // Call the startNewRound() function or perform any other necessary logic
    
      // Add the CSS class with the animation
      elementToAnimate.classList.add("vs-animation");
    
      // Wait for the duration of the animation and then remove the class to allow it to be applied again
      setTimeout(() => {
        elementToAnimate.classList.remove("vs-animation");
      }, 1000); // Adjust the value to match the duration of your animation in milliseconds
    });
    

    In this example, we are adding the vs-animation class to the element you want to animate when the button is clicked. Then, we are removing that class after a specified time so that the animation can be restarted by clicking again.

    Make sure to adjust the time in setTimeout to match the duration of your animation in milliseconds.

    Finally, ensure that the element you want to animate has the vs-animation class initially so that the animation is ready to run. anthoncode.com

    Login or Signup to reply.
  2. Web Animations API

    I recommend the modern way, which would be to use the Web Animations API: The element.animate() method allows you to specify keyframes and options.

    Starting a new animation cancels the previous automatically.

    Example (playing a new animation per click):

    const button = document.querySelector("button");
    const box = document.querySelector(".box");
    
    button.addEventListener("click", () => {
      const keyframes = [
        { top: 0 },
        { top: "4rem" }
      ];
      box.animate(keyframes, { duration: 2000 });
    });
    .box {
      position: relative;
      width: 4rem;
      height: 4rem;
      background-color: red;
    }
    <button>Start animation</button>
    <div class="box"></div>

    Alternative example (restarting one animation on click):

    const button = document.querySelector("button");
    const box = document.querySelector(".box");
    
    const animation = box.animate(
      [
        { top: 0 },
        { top: "4rem" }
      ],
      { duration: 2000 }
    );
    animation.cancel();
    
    button.addEventListener("click", () => {
      animation.cancel();
      animation.play();
    });
    .box {
      position: relative;
      width: 4rem;
      height: 4rem;
      background-color: red;
    }
    <button>Start animation</button>
    <div class="box"></div>

    Here is an example of how you could get a @keyframes at-rule’s keyframes using the CSSOM API:

    const vsAnimation = getKeyframesOf("vs-animation");
    
    console.log(vsAnimation);
    
    function getKeyframesOf(animationName) {
      function collectDeclarations(keyframeRule) {
        const keyframe = { offset: parseFloat(keyframeRule.keyText) / 100 };
        for (const prop of keyframeRule.style) {
          switch (prop) {
          case "offset":
            // Exception for CSS `offset` property:
            // See https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API/Keyframe_Formats#attributes
            keyframe.cssOffset = keyframeRule.style.offset;
            break;
          default:
            keyframe[prop] = keyframeRule.style[prop];
          }
        }
        return keyframe;
      }
    
      let keyframesRule = null;
      for (const sheet of document.styleSheets) {
        for (const rule of sheet.cssRules) {
          if (rule instanceof CSSKeyframesRule === false) continue;
          if (rule.name !== animationName) continue;
    
          keyframesRule = rule;
        }
      }
      
      if (!keyframesRule) return null;
    
      return Array.from(keyframesRule.cssRules).map(collectDeclarations);
    }
    @keyframes vs-animation {
      from { top: 0 }
      to { top: 4rem }
    }

    Note: It should work as intended for most cases, but I am not fully certain that it works for every case.

    CSS Animations

    Alternatively, you can use CSS @keyframes at-rules to specify the animation’s name and keyframes.

    Playing the animation requires at least the name and a duration. These can be given with the animation shorthand property, or the respective individual properties.

    Example:

    const button = document.querySelector("button");
    const box = document.querySelector(".box");
    
    button.addEventListener("click", () => {
      box.classList.add("animated");
    });
    .box {
      position: relative;
      width: 4rem;
      height: 4rem;
      background-color: red;
    }
    
    @keyframes vs-animation {
      from { top: 0 }
      to { top: 4rem }
    }
    
    .animated {
      animation-name: vs-animation;
      animation-duration: 2s;
    }
    <button>Start animation</button>
    <div class="box"></div>

    Note:
    Web Animations API uses "linear" for the easing option by default.
    CSS animations use ease for animation-timing-function by default.

    Unfortunately, to enable replay we cannot simply remove then add the animation-related declarations. Before adding them back, we have to wait for a reflow; for the browser to check for CSS changes.

    Example of simply removing then adding (which does not replay the animation):

    const button = document.querySelector("button");
    const box = document.querySelector(".box");
    
    button.addEventListener("click", () => {
      box.classList.remove("animated");
      box.classList.add("animated");
    });
    .box {
      width: 2rem;
      height: 2rem;
      background-color: red;
    }
    
    @keyframes vs-animation {
      0% {
        position: relative;
        top: 0rem;
      }
      100% {
        position:relative;
        top: 2rem;
      }
    }
    
    .animated {
      animation-name: vs-animation;
      animation-duration: 2s;
    }
    <button>Start animation</button>
    <div class="box"></div>

    Changes to the CSS will automatically cause a reflow before a frame, but after JS. That means waiting for one frame before re-adding the declarations will work: See MDN’s Run an animation again example.

    Note: Generally, we have to wait for a reflow (the "layout engine"), not repaint (the "rendering engine"). @AdamLeggett comments that "requestAnimationFrame waits for the rendering engine. [Prefer the animate function]; the second most technically correct is unfortunately void element.offsetWidth."
    The void element.offsetWidth trick is shown below. But again, it is best to use the Web Animations API.

    But we can cause a reflow manually as well; here is a list of JS-related reflow causes. As the link mentions:

    Generally, all APIs that synchronously provide layout metrics
    will trigger forced reflow / layout.

    That means, by reading offsetWidth (a layout metric) we cause a reflow. The statement is often seen with void to discard the value, to make it conform to strict mode[Can’t find citation again].

    Ideally, you wouldn’t manually reflow (too often), especially not in time-sensitive applications. However, it does make for arguably simpler (though more "magical") code:

    const button = document.querySelector("button");
    const box = document.querySelector(".box");
    
    button.addEventListener("click", () => {
      box.classList.remove("animated");
      void box.offsetHeight; // Reflow
      box.classList.add("animated");
    });
    .box {
      position: relative;
      width: 4rem;
      height: 4rem;
      background-color: red;
    }
    
    @keyframes vs-animation {
      0% { top: 0rem }
      100% { top: 4rem }
    }
    
    .animated {
      animation-name: vs-animation;
      animation-duration: 2s;
    }
    <button>Start animation</button>
    <div class="box"></div>

    The Web animations API is meant for controlling animations via JS; fitting for your use case. Unfortunately, it doesn’t (yet?) offer a way to convert a @keyframes at-rule to the keyframe format natively.

    In contrast, with the approach relying on CSS animations you have to watch out for caveats (waiting for a reflow inbetween) and technicalities (requestAnimationFrame waiting for render, not reflow), and have to use workarounds (reading layout metrics).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search