Динамически добавленная SVG-анимация иногда не срабатывает

В этом примере шар (круг SVG) создается каждый раз, когда указатель мыши наводится на серый квадрат. К мячу добавляется анимация, идущая сверху слева направо, а затем запускается.

Иногда, по крайней мере, в последней версии Chrome, мяч создается, но анимация не отображается до тех пор, пока не произойдет следующее событие. Где я ошибаюсь?

function anim() {
  const circ = document.createElementNS("http://www.w3.org/2000/svg", "circle");
  circ.setAttribute("class", "ball");
  circ.setAttribute("cx", 0);
  circ.setAttribute("cy", 0);
  const anim = document.createElementNS("http://www.w3.org/2000/svg", "animateTransform");
  anim.setAttribute("attributeName", "transform");
  anim.setAttribute("type", "translate");
  anim.setAttribute("begin", "indefinite");
  anim.setAttribute("from", "0,0");
  anim.setAttribute("to", "300,300");
  anim.setAttribute("dur", "2s");
  anim.setAttribute("repeatCount", "1");
  anim.addEventListener("endEvent", (e) => {
    e.target.parentElement.remove();
  });
  circ.append(anim);
  test.append(circ);
  anim.beginElement();
}
const test = document.querySelector("#test");
test.addEventListener("mouseenter", anim);
#test {
  background-color: lightgray;
  height: 300px;
  width: 300px;
}

.ball {
  fill: black;
  r: 10px;
}
<svg id = "test" />
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
0
53
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Похоже, что проблемы возникают из-за того, что анимация активируется до того, как SVG был перерисован.

В этом фрагменте добавление элементов в SVG начинается после того, как система получит возможность перерисовать измененный SVG с его новыми атрибутами.

<!DOCTYPE html>
<html>

<head>
  <style>
    #test {
      background-color: lightgray;
      height: 300px;
      width: 300px;
    }
    
    .ball {
      fill: black;
      r: 10px;
    }
  </style>
</head>

<body>
  <svg id = "test" />
  <script>
    function anim() {
      const circ = document.createElementNS("http://www.w3.org/2000/svg", "circle");
      circ.setAttribute("class", "ball");
      circ.setAttribute("cx", 0);
      circ.setAttribute("cy", 0);
      const anim = document.createElementNS("http://www.w3.org/2000/svg", "animateTransform");
      anim.setAttribute("attributeName", "transform");
      anim.setAttribute("type", "translate");
      anim.setAttribute("begin", "indefinite");
      anim.setAttribute("from", "0,0");
      anim.setAttribute("to", "300,300");
      anim.setAttribute("dur", "2s");
      anim.setAttribute("repeatCount", "1");
      anim.addEventListener("endEvent", (e) => {
        e.target.parentElement.remove();
      });
      requestAnimationFrame(function() {
        circ.append(anim);
        test.append(circ);
        anim.beginElement();
      });
    }
    const test = document.querySelector("#test");
    test.addEventListener("mouseenter", anim);
  </script>
</body>

</html>

Запуск перекомпоновки (например, document.body.offsetWidth;) также работает и кажется более надежным, поскольку rAF сработает перед следующей перерисовкой и, возможно, перед следующим пересчетом.

Kaiido 20.06.2024 08:22

@Кайидо, это интересно. Я не видел проблем с requestAnimationFrame и думал, что он, по сути, имеет тот же эффект перекомпоновки - это означает, что я не полностью уловил порядок событий, я думаю!

A Haworth 20.06.2024 08:29

Ни один РАФ не срабатывает до следующей окраски и до пересчета. Здесь даже простой setTimeout() тоже работает, поэтому я не уверен на 100%, что происходит, и у меня нет времени разбираться. Тем не менее, синхронный запуск перекомпоновки обычно является наиболее надежным решением, поскольку все асинхронные решения могут фактически работать, потому что произошло что-то еще, и не факт, что это что-то еще будет происходить всегда. Но в любом случае, если бы кто-то смог открыть проблему, это было бы хорошо.

Kaiido 20.06.2024 08:38

@Кайидо, спасибо за объяснение. Теперь я немного прояснился и согласен, что следует использовать определенную принудительную синхронную перекомпоновку или, возможно, двойной rAF, как, например. обсуждается в stackoverflow.com/questions/75527696/…

A Haworth 20.06.2024 22:43

Другие вопросы по теме