3 回答

TA貢獻1872條經驗 獲得超4個贊
免責聲明:我不認為 jQuery 對回答這個問題很重要,如果其他人在看到這個答案后選擇依賴此代碼,它會損害負載和運行時性能。因此,我將使用 vanilla JavaScript 來回答,以幫助盡可能多的人,但如果您想使用 jQuery,您仍然可以應用相同的概念。
答:沒有動畫隊列,但您可以自己制作。
例如,您可以使用閉包和/或 a Map(在下面的代碼段中,我實際上使用 aWeakMap來幫助垃圾收集)將有關動畫的數據鏈接到目標元素。如果您將動畫狀態保存為true完成時,您可以檢查并最終在全部為 時觸發不同的回調true,或者調度您自己的自定義事件。我使用了自定義事件方法,因為它更靈活(能夠添加多個回調)。
以下代碼還可以幫助您避免在您實際上只關心幾個特定動畫的情況下等待所有動畫。它還應該讓您多次處理多個單獨元素的動畫事件(嘗試運行代碼段并單擊幾次框)
const addAnimationEndAllEvent = (() => {
const weakMap = new WeakMap()
const initAnimationsObject = (element, expectedAnimations, eventName) => {
const events = weakMap.get(element)
const animationsCompleted = {}
for (const animation of expectedAnimations) {
animationsCompleted[animation] = false
}
events[eventName] = animationsCompleted
}
return (element, expectedAnimations, eventName = 'animationendall') => {
if (!weakMap.has(element)) weakMap.set(element, {})
if (expectedAnimations) {
initAnimationsObject(element, expectedAnimations, eventName)
}
// When any animation completes...
element.addEventListener('animationend', ({ target, animationName }) => {
const events = weakMap.get(target)
// Use all animations, if there were none provided earlier
if (!events[eventName]) {
initAnimationsObject(target, window.getComputedStyle(target).animationName.split(', '), eventName)
}
const animationsCompleted = events[eventName]
// Ensure this animation should be tracked
if (!(animationName in animationsCompleted)) return
// Mark the current animation as complete (true)
animationsCompleted[animationName] = true
// If every animation is now completed...
if (Object.values(animationsCompleted).every(
isCompleted => isCompleted === true
)) {
const animations = Object.keys(animationsCompleted)
// Fire the event
target.dispatchEvent(new CustomEvent(eventName, {
detail: { target, animations },
}))
// Reset for next time - set all animations to not complete (false)
initAnimationsObject(target, animations, eventName)
}
})
}
})()
const toggleAnimation = ({ target }) => {
target.classList.toggle('being-animated')
}
document.querySelectorAll('.animatable').forEach(element => {
// Wait for all animations before firing the default event "animationendall"
addAnimationEndAllEvent(element)
// Wait for the provided animations before firing the event "animationend2"
addAnimationEndAllEvent(element, [
'animateOpacity',
'animatePosition'
], 'animationend2')
// Listen for our added "animationendall" event
element.addEventListener('animationendall', ({detail: { target, animations }}) => {
console.log(`Animations: ${animations.join(', ')} - Complete`)
})
// Listen for our added "animationend2" event
element.addEventListener('animationend2', ({detail: { target, animations }}) => {
console.log(`Animations: ${animations.join(', ')} - Complete`)
})
// Just updated this to function on click, so we can test animation multiple times
element.addEventListener('click', toggleAnimation)
})
.animatable {
margin: 5px;
width: 100px;
height: 100px;
background: black;
}
@keyframes animateOpacity {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes animatePosition {
0% {
transform: translate3d(0, 0, 0);
}
100% {
transform: translate3d(0, 15px, 0);
}
}
@keyframes animateRotation {
100% {
transform: rotate(360deg);
}
}
.animatable.being-animated {
animation:
animateOpacity 1s ease 0s forwards,
animatePosition 1.5s ease 0s forwards,
animateRotation 2s ease 0s forwards;
}
<div class="animatable"></div>
<div class="animatable"></div>

TA貢獻2037條經驗 獲得超6個贊
它當然值得成為公認的答案。也就是說,我受到啟發,想看看一種不那么冗長的方法是否可行。這是我想出的。
這是不言自明的,但基本上概念是所有動畫屬性的索引都是相關的,我們可以使用它來查找最后完成的動畫的名稱。
const getFinalAnimationName = el => {
const style = window.getComputedStyle(el)
// get the combined duration of all timing properties
const [durations, iterations, delays] = ['Duration', 'IterationCount', 'Delay']
.map(prop => style[`animation${prop}`].split(', ')
.map(val => Number(val.replace(/[^0-9\.]/g, ''))))
const combinedDurations = durations.map((duration, idx) =>
duration * iterations[idx] + delays[idx])
// use the index of the longest duration to select the animation name
const finalAnimationIdx = combinedDurations
.findIndex(d => d === Math.max(...combinedDurations))
return style.animationName.split(', ')[finalAnimationIdx]
}
// pipe your element through this function to give it the ability to dispatch the 'animationendall' event
const addAnimationEndAllEvent = el => {
const animationendall = new CustomEvent('animationendall')
el.addEventListener('animationend', ({animationName}) =>
animationName === getFinalAnimationName(el) &&
el.dispatchEvent(animationendall))
return el
}
// example usage
const animatable = document.querySelector('.animatable')
addAnimationEndAllEvent(animatable)
.addEventListener('animationendall', () => console.log('All animations have finished'))
.animatable {
width: 50px;
height: 50px;
background-color: red;
position: relative;
left: 0;
animation: 1.5s slidein, 1s fadein;
}
@keyframes slidein {
0% { left: 100vw; }
100% { left: 0; }
}
@keyframes fadein {
0% { opacity: 0; }
100% { opacity: 1; }
}
<div class="animatable"></div>
添加回答
舉報