animation-on-scroll

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Animation On Scroll Skill

滚动触发动画技能

Workflow

工作流程

  1. Confirm animation style, timing, and whether animations should run once or repeat.
  2. Provide the keyframes + JS observer snippet and the exact Tailwind class to apply.
  3. Offer focused tweaks only (threshold, rootMargin, duration, delay, transform/blur values).
  1. 确认动画样式、时长,以及动画应仅运行一次还是重复运行。
  2. 提供关键帧 + JS观察者代码片段,以及要应用的精确Tailwind类。
  3. 仅提供针对性调整选项(阈值、rootMargin、时长、延迟、变换/模糊值)。

Usage checklist

使用检查清单

  • Insert the JS snippet in the
    <head>
    after the keyframes.
  • Add the animation class and
    animate-on-scroll
    to elements.
  • Ensure your keyframes name matches the Tailwind animation reference.
  • 在关键帧代码之后,将JS代码片段插入
    <head>
    中。
  • 为元素添加动画类和
    animate-on-scroll
    类。
  • 确保关键帧名称与Tailwind动画引用匹配。

IntersectionObserver trigger

IntersectionObserver trigger

html
<script>
  /*
    Sequence animation on scroll when visible. Requires Animation Keyframe. Usage:

    1) Insert this code in the <head> along with the Animation Keyframe code.

    2) Add to Tailwind Classes: [animation:animationIn_0.8s_ease-out_0.1s_both] animate-on-scroll
  */
  (function () {
    // Inject CSS for paused/running states
    const style = document.createElement("style");
    style.textContent = `
      /* Default: paused */
      .animate-on-scroll { animation-play-state: paused !important; }
      /* Activated by JS */
      .animate-on-scroll.animate { animation-play-state: running !important; }
    `;
    document.head.appendChild(style);

    const once = true;

    if (!window.__inViewIO) {
      window.__inViewIO = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            entry.target.classList.add("animate");
            if (once) window.__inViewIO.unobserve(entry.target);
          }
        });
      }, { threshold: 0.2, rootMargin: "0px 0px -10% 0px" });
    }

    window.initInViewAnimations = function (selector = ".animate-on-scroll") {
      document.querySelectorAll(selector).forEach((el) => {
        window.__inViewIO.observe(el); // observing twice is a no-op
      });
    };

    document.addEventListener("DOMContentLoaded", () => initInViewAnimations());
  })();
</script>
html
<script>
  /*
    Sequence animation on scroll when visible. Requires Animation Keyframe. Usage:

    1) Insert this code in the <head> along with the Animation Keyframe code.

    2) Add to Tailwind Classes: [animation:animationIn_0.8s_ease-out_0.1s_both] animate-on-scroll
  */
  (function () {
    // Inject CSS for paused/running states
    const style = document.createElement("style");
    style.textContent = `
      /* Default: paused */
      .animate-on-scroll { animation-play-state: paused !important; }
      /* Activated by JS */
      .animate-on-scroll.animate { animation-play-state: running !important; }
    `;
    document.head.appendChild(style);

    const once = true;

    if (!window.__inViewIO) {
      window.__inViewIO = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            entry.target.classList.add("animate");
            if (once) window.__inViewIO.unobserve(entry.target);
          }
        });
      }, { threshold: 0.2, rootMargin: "0px 0px -10% 0px" });
    }

    window.initInViewAnimations = function (selector = ".animate-on-scroll") {
      document.querySelectorAll(selector).forEach((el) => {
        window.__inViewIO.observe(el); // observing twice is a no-op
      });
    };

    document.addEventListener("DOMContentLoaded", () => initInViewAnimations());
  })();
</script>

Keyframes

关键帧

html
<style>
  /*
    Sequence animation intro. Usage:

    1) Insert this code in the <head>

    2) Add to Tailwind Classes: [animation:animationIn_0.8s_ease-out_0.1s_both]
  */
  @keyframes animationIn {
    0% {
      opacity: 0;
      transform: translateY(30px);
      filter: blur(8px);
    }

    100% {
      opacity: 1;
      transform: translateY(0);
      filter: blur(0px);
    }
  }
</style>
html
<style>
  /*
    Sequence animation intro. Usage:

    1) Insert this code in the <head>

    2) Add to Tailwind Classes: [animation:animationIn_0.8s_ease-out_0.1s_both]
  */
  @keyframes animationIn {
    0% {
      opacity: 0;
      transform: translateY(30px);
      filter: blur(8px);
    }

    100% {
      opacity: 1;
      transform: translateY(0);
      filter: blur(0px);
    }
  }
</style>

Tailwind example

Tailwind示例

html
<div class="animate-on-scroll [animation:animationIn_0.8s_ease-out_0.1s_both]">
  ...
</div>
html
<div class="animate-on-scroll [animation:animationIn_0.8s_ease-out_0.1s_both]">
  ...
</div>

Customization knobs

自定义调整项

  • Trigger: adjust
    threshold
    and
    rootMargin
    for earlier/later reveals.
  • Repeat: set
    once = false
    to allow replays when re-entering.
  • Motion: tweak
    translateY
    and
    blur
    in keyframes.
  • Timing: change duration and delay in the Tailwind animation value.
  • 触发时机:调整
    threshold
    rootMargin
    以提前或延迟显示动画。
  • 重复设置:将
    once = false
    设置为允许元素重新进入视口时重播动画。
  • 运动效果:调整关键帧中的
    translateY
    blur
    值。
  • 时间设置:修改Tailwind动画值中的时长和延迟。

Common pitfalls

常见误区

  • Forgetting to include the keyframes before the JS snippet.
  • Using a different keyframe name than in the Tailwind animation.
  • Animations not running because the element is already in view before observer init.
  • 忘记在JS代码片段之前引入关键帧代码。
  • 使用与Tailwind动画中不同的关键帧名称。
  • 动画不运行,因为元素在观察者初始化前已在视口中。

Questions to ask when specs are missing

当缺少规格时需询问的问题

  • Should animations run once or every time the element re-enters?
  • How far before entering the viewport should they start?
  • What motion style (fade, slide, blur, scale) do you want?
  • 动画应仅运行一次,还是每次元素重新进入视口时都运行?
  • 元素进入视口前多久开始动画?
  • 你想要哪种运动样式(淡入、滑动、模糊、缩放)?