Animate.css promise 有时不会在 animationEnd 后清除 类

Animate.css promise sometimes doesn't clean the classes after animationEnd

我的页面中的标题很少,我使用与 Animate.css 不同的动画对每个标题进行动画处理。 首先,我将标题上的每个字母都包装在一个 span 元素中,然后我在 parent 上使用一个事件侦听器来查看它何时结束。 我使用 event.target 来确定哪个字母悬停在它上面,并使用 parent 节点来确定它的 class 是 parent 并给出相应的动画。

问题是有时有些字母没有动画。我看到这封信仍然有以前悬停的“animate__animated animate__(动画名称)”。

我使用来自 Animate.css 使用 promises 的网站的 JS 代码。

有谁知道为什么有时 classes 没有清理干净?我看到我正在尝试为很多元素制作动画,但我并没有将所有元素一起制作动画,最后我使用冒泡事件来 avit 我的事件列表器。

   /// wrap every letter in a span
  const words = document.querySelectorAll(".word");
  words.forEach(function (e) {
    e.innerHTML = e.textContent.replace(
      /\bCristian\b|([^\x00-\x40]|\w|\S)/g,
      (match, group) =>
        group == undefined
          ? `<span class="cristian">${match}</span>`
          : `<span class="letter">${match}</span>`
    );
  });
 // anim letters function
  const animationsSettings = [
    ["fede", "rubberBand"],
    ["myne", "bounce"],
    ["skills", "jello"],
    ["contact", "wobble"],
    ["tools", "swing"],
  ];
  const animationPrefix = "animate__";
  const defAnimationName = "jello";
  let animationName = defAnimationName;

  const regexClasses = () => {
    let regex = ``;
    for (settings of animationsSettings) {
      regex += `\b${settings[0]}\b|`;
    }
    return regex.slice(0, -1);
  };

  const regexConst = new RegExp(regexClasses(), "");

  function anim_letters(el, i) {
    // check if el is an event or not
    const node =
      el.originalEvent instanceof Event || el.target ? el.target : el;
    const parentClasses = node.parentNode.classList.value;
    const parentClassMatch = parentClasses.match(regexConst);

    if (
      node.tagName == "SPAN" &&
      node.className !== "word" &&
      node.className !== "cristian"
    ) {
      if (parentClassMatch) {
        for (settings of animationsSettings) {
          if (parentClassMatch[0] == settings[0]) {
            animationName = settings[1];
            break;
          }
        }
      } else {
        animationName = defAnimationName;
      }

      // We create a Promise and retu1rn it
      new Promise((resolve, reject) => {
        if (i) {
          animationName = "jello";
          node.style.animationDelay = `${i * 0.1 + 0.2}s`;
        }
        node.classList.add(
          "animate__animated",
          `${animationPrefix}${animationName}`
        );

        // When the animation ends, we clean the classes and resolve the Promise
        function handleAnimationEnd(event) {
          event.stopPropagation();
          node.classList.remove(
            "animate__animated",
            `${animationPrefix}${animationName}`
          );
          node.style.animationDelay = "";
          resolve("animation ended");
        }

        node.addEventListener("animationend", handleAnimationEnd, {
          once: true,
        });
      });
    }
  }

  const lettersStart = document.querySelectorAll(".lettersstart>.letter");
  for (const [i, letter] of lettersStart.entries()) {
    anim_letters(letter, i);
  }

  for (e of words) {
    e.addEventListener("mouseover", anim_letters);
  }
.letter{
display:inline-block
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" rel="stylesheet"/>
<h2 class="lettersstart word hi">Hi,</h2>
<h2 class="lettersstart im word">I'm
<span class="cristian">Cristian</span></h2>
<h3 class="myne word">Myne<wbr>social</h3>
<h3 class="fede word">Federico<wbr>Angeli</h3>

可以看到真实页面here

谢谢

我认为问题在于您的 animationName 变量是全局变量。当您为带有 class A 的字母制作动画时,然后在第一个动画完成之前将鼠标悬停在带有不同 class B 的字母上时,它将尝试从元素中删除 class B结束并卡在 class A 上。您应该可以通过简单地移动

来解决这个问题
let animationName = defAnimationName;

声明到 anim_letters 函数中。

/// wrap every letter in a span
const words = document.querySelectorAll(".word");
words.forEach(function (e) {
  e.innerHTML = e.textContent.replace(
    /\bCristian\b|([^\x00-\x40]|\w|\S)/g,
    (match, group) =>
      group == undefined
        ? `<span class="cristian">${match}</span>`
        : `<span class="letter">${match}</span>`
  );
  // FIXME: XSS issue if textContent contains <>
});

// anim letters function
// use a simple lookup map instead of complicated and fragile regex stuff
const animationsSettings = new Map([
  ["fede", "rubberBand"],
  ["myne", "bounce"],
  ["skills", "jello"],
  ["contact", "wobble"],
  ["tools", "swing"],
]);
const animationPrefix = "animate__";
const defAnimationName = "jello";

function anim_letters(el, i) {
  // check if el is an event or not
  const node = el.originalEvent instanceof Event || el.target ? el.target : el;

  if (node.tagName != "SPAN" || node.classList.contains("word") || node.classList.contains("cristian")) {
    return Promise.resolve();
  }
  let animationName = defAnimationName
  for (const className of node.parentNode.classList) {
    if (animationsSettings.has(className)) {
      animationName = animationsSettings.get(className);
      break;
    }
  }
  if (i) {
    animationName = "jello";
    node.style.animationDelay = `${i * 0.1 + 0.2}s`;
  }
  return new Promise((resolve, reject) => {
    node.classList.add(
      "animate__animated",
      `${animationPrefix}${animationName}`
    );
    // When the animation ends, we clean the classes and resolve the Promise
    function handleAnimationEnd(event) {
      event.stopPropagation();
      node.classList.remove(
        "animate__animated",
        `${animationPrefix}${animationName}`
      );
      node.style.animationDelay = "";
      resolve("animation ended");
    }
    node.addEventListener("animationend", handleAnimationEnd, {
      once: true,
    });
  });
}

const lettersStart = document.querySelectorAll(".lettersstart>.letter");
for (const [i, letter] of lettersStart.entries()) {
  anim_letters(letter, i);
}

for (e of words) {
  e.addEventListener("mouseover", anim_letters);
}
.letter {
  display:inline-block
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" rel="stylesheet"/>
<h2 class="lettersstart word hi">Hi,</h2>
<h2 class="lettersstart im word">I'm
<span class="cristian">Cristian</span></h2>
<h3 class="myne word">Myne<wbr>social</h3>
<h3 class="fede word">Federico<wbr>Angeli</h3>