Как правильно обрабатывать события, связанные с методом animate в Web Animation API?

В <a href="https://codepen.io/DazzRune/pen/rNXvRjW" rel="nofollow">примере</a> я реализую обработку трех событий: <code>mouseenter</code>, который скейлит элемент; <code>mousemove</code>, который разворачивает его; и <code>mouseleave</code>, который возвращает элемент в исходное состояние. Однако в <a href="https://youtu.be/3EaPva9NHuQ" rel="nofollow">видео</a> с проблемой можно заметить, что в последние секунды элемент не возвращается в свое первоначальное положение. Я обнаружил, что такое поведение чаще всего возникает, когда событие <code>mousemove</code> триггерится очень быстро внутри элемента. Реже это происходит при быстром чередовании событий <code>mouseenter</code> и <code>mouseleave</code>. <br/><br/> Примечание: исключение метода очистки кадров <code>overAnimation.cancel()</code> в обработчике onLeave позволяет решить проблему, однако это приводит к сбоям в другой анимации, которая применяется к родительскому Vue-компоненту <code>&lt;transition-group&gt;</code>. Как мне корректно обработать события в таком случае? <br/><br/> <code class="html">&lt;div class="item"&gt;&lt;/div&gt;</code> <br/> <pre><code class="javascript">const itemRef = document.getElementById('item')
let overAnimation = null

itemRef.addEventListener('mouseleave', (e) =&gt; {
    overAnimation = itemRef.animate([
        {
            transform: 'none',
        }
    ], {duration: 1000, fill: 'forwards'})

    overAnimation.onfinish = () =&gt; {
        overAnimation.cancel()
    }
})

itemRef.addEventListener('mousemove', (e) =&gt; {
    const x = e.offsetX
    
    overAnimation = itemRef.animate([
        {
            transform: `scale(1.6) rotate(${x}deg)`,
        }
    ], {duration: 1000, fill: 'forwards'})
})

itemRef.addEventListener('mouseenter', (e) =&gt; {
    overA
  • 31 октября 2024 г. 21:42
Ответы на вопрос 2
Чтобы улучшить обработку событий с использованием метода `animate` в Web Animation API и избежать проблем, связанных с конфликтующими анимациями, вы можете использовать несколько подходов.

### 1. Удаление предыдущих анимаций

Одной из причин, по которой анимация не возвращалась в первоначальное положение, является то, что предыдущая анимация не была завершена или была отменена некорректно. Чтобы избежать этого, вы можете отменять предыдущую анимацию каждый раз перед тем, как запустить новую.

### 2. Использование флага для управления состоянием анимации

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

### 3. Добавление `animation.cancel()` в правильных местах

Вместо того, чтобы использовать `onfinish`, вы можете отменить анимацию непосредственно перед ее созданием, чтобы убедиться, что предыдущая анимация завершена.

### Рекомендованный код

Вот пример того, как можно переписать ваш исходный код с учетом этих подходов:

```javascript
const itemRef = document.getElementById('item')
let overAnimation = null
let isAnimating = false

function cancelAnimation() {
    if (overAnimation) {
        overAnimation.cancel()
        overAnimation = null
    }
}

itemRef.addEventListener('mouseleave', (e) => {
    cancelAnimation()
    
    overAnimation = itemRef.animate([
        {
            transform: 'none',
        }
    ], { duration: 1000, fill: 'forwards' })
})

itemRef.addEventListener('mousemove', (e) => {
    const x = e.offsetX

    cancelAnimation() // Отменяем предыдущую анимацию перед запуском новой
    
    overAnimation = itemRef.animate([
        {
            transform: `scale(1.6) rotate(${x}deg)`,
        }
    ], { duration: 1000, fill: 'forwards' })
})

itemRef.addEventListener('mouseenter', (e) => {
    cancelAnimation() // Отменяем предыдущую анимацию при входе мыши

    overAnimation = itemRef.animate([
        {
            transform: 'scale(1.6) rotate(0deg)',
        }
    ], { duration: 1000, fill: 'forwards' })
})
```

### Дополнительные советы:

- Убедитесь, что ваши обработчики событий проверяют состояние анимации и управляют его запуском корректно.
- При необходимости добавьте задержку между событиями `mouseenter` и `mouseleave`, чтобы уменьшить количество быстро повторяющихся событий. Вы можете использовать `setTimeout` для создания задержки.
- Рассмотрите возможность использования CSS-классов для анимаций вместо JavaScript, что может упростить код и повысить производительность.

Этот подход должен помочь вам исправить проблемы с анимацией и сделать ее более плавной и предсказуемой.
Порядок срабатывания эвентов не гарантирует, что порядок запуска анимации будет такой же. 
Не знаю как понятнее объяснить ...

Но оставлю тут исправный вариант )
const itemRef = document.querySelector('.item')
let move = (e) => {
    const x = e.offsetX
    itemRef.animate([{
        transform: `scale(1.6) rotate(${x}deg)`,
    }], {duration: 1000, fill: 'forwards'})
}

itemRef.addEventListener('mouseleave', (e) => {
    itemRef.removeEventListener('mousemove', move)
    itemRef.animate([{
        transform: 'none',
    }], {duration: 1000, fill: 'forwards'})
})

itemRef.addEventListener('mouseenter', (e) => {
    itemRef.addEventListener('mousemove', move)
})


P/S
что там с этим overAnimation.cancel() - не знаю ... оно точно надо?
анимация отмененная после завершения что то меняет?
Похожие вопросы