3 回答
TA贡献1872条经验 获得超3个赞
免责声明:我不认为 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>
添加回答
举报