通过webkitAnimationEnd实现轮播动画
说到轮播动画,大家应该都知道swiper
,我们也常常通过它来实现大部分轮播动画,但有时候项目比较简单,以及一些版本的问题,我们可以尝试一些其他简单的手段来实现轮播。比如webkitAnimationEnd
。
一、webkitAnimationEnd是什么
在WebKit引擎的浏览器中,当CSS3的animation
动画执行结束时,会触发webkitAnimationEnd
事件。
在以下示例中,当执行完一次animation
动画,控制台就会触发webkitAnimationEnd
事情,打印出“执行完一次animation”的字样。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
margin:0;
padding: 0;
}
.container {
width: 200px;
white-space: nowrap;
overflow: hidden;
font-size: 20px;
color: #65b4ae;
margin: 20px;
}
.container_words {
position: relative;
width: fit-content;
animation: move 4s linear;
padding-left: 20px;
}
.container_words::after {
position: absolute;
right: -100%;
content: attr(text);
}
@keyframes move {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-100%);
}
}
</style>
</head>
<body>
<div class="container">
<p class="container_words" text="文字文字文字文字文字" id="word">
文字文字文字文字文字
</p>
</div>
</body>
<script>
const word = document.querySelector('#word')
const animateEvent = () => {
console.log('执行完一次animation')
}
word.addEventListener('webkitAnimationEnd', animateEvent)
</script>
</html>
除此之外,还有webkitAnimationStart
事件,它与webkitAnimationEnd
相对应,一个是开始,一个是结束。
以及webkitAnimationIteration
事件,监听重复运动事件,每执行完一次animation
触发一次。
const animateEndEvent = () => {
console.log('执行完一次animation')
}
const animateIterationEvent = () => {
console.log('执行完一次循环animation')
}
// 设置animation: move 4s linear infinite; 无限循环的状态下,此事件触发不了
word.addEventListener('webkitAnimationStart', animateStartEvent)
// 设置animation: move 4s linear infinite; infinite便可监听到
word.addEventListener('webkitAnimationIteration', animateIterationEvent )
二、webkitAnimationEnd实现轮播
那我们在实现轮播逻辑时,可以利用CSS3的动画属性结合其监听来实现。
以下为一个简单示例:
<template>
<div class="task">
<div
:class="{ move }"
@mouseover="pauseAni"
@mouseout="playAni"
ref="moveRow"
>
<div
class="task_item"
v-for="(item, index) in data"
:key="index"
>{{ item }}</div>
</div>
</div>
</template>
<script>
import { reactive, toRefs, onMounted, nextTick, onUnmounted } from "vue";
export default {
name: 'animateTest',
setup() {
const state = reactive({
data: [1, 2, 3, 4, 5, 6],
// 控制当前是否运动
move: false,
// 运动的元素DOM
moveRow: null
});
let thread = null;
const animateEvent = () => {
// 基本逻辑,根据需求而定,此示例实现的上下一个一个的轮播
const newList = JSON.parse(JSON.stringify(state.data));
newList.push(newList.shift());
state.data = newList;
state.move = false;
}
const playAni = () => {
// 判断一个数字,根据需求而定
if (state.data.length > 2) {
thread = setInterval(() => {
state.move = true;
}, 5000);
// 监听webkitAnimationEnd事件
state.moveRow.addEventListener('webkitAnimationEnd', animateEvent);
}
};
const clearTime = () => {
if (thread) {
clearInterval(thread);
thread = null;
}
}
const initAnimate = async () => {
// 初始化动画
clearTime();
await nextTick();
playAni();
};
const pauseAni = () => {
clearTime();
};
onMounted(() => {
initAnimate();
});
onUnmounted(() => {
// 清除监听
state.moveRow && state.moveRow.removeEventListener('webkitAnimationEnd', animateEvent)
clearTime();
})
return {
...toRefs(state),
pauseAni,
playAni,
};
},
};
</script>
<style lang="scss" scoped>
.task {
position: relative;
width: 488px;
height: 284px;
padding: 2px 25px 3px 18px;
box-sizing: border-box;
margin-top: 4px;
background-color: rgb(213, 204, 204);
overflow: hidden;
.move {
animation: slideAnimate 0.5s linear;
}
&_item {
width: 100%;
height: 142px;
padding: 16px 0 17px 0;
border-bottom: 1px dashed rgba(35, 93, 105, 1);
color: #fff;
text-align: center;
line-height: 142px;
}
}
@keyframes slideAnimate {
from {
transform: translateY(0);
}
to {
transform: translateY(-142px);
}
}
</style>
效果如下:
二、webkitAnimationIteration实现轮播
那么根据上面的例子,我们也可以尝试使用webkitAnimationIteration
改造,说不定可以达到更好的效果?
- 第一步就需要让其循环执行。
但是我们发现,循环执行后,它会一直执行动画,而不能达到我们想要的:每执行一次动画前都停顿一段时间。而animation-delay
只能设置设置动画在启动前的延迟间隔,它仅仅是在动画开启的第一次才会有暂停效果。
但是我们可以另辟蹊径,如果我们的动画时间是0.5s
,停顿时间是4.5s
,那么我们可以采用设置运动时间为5s
,但是让其前4.5s
是不动的。例如:
.move {
animation: slideAnimate 5s linear infinite;
/* animation: slideAnimate1 0.5s linear infinite; */
}
@keyframes slideAnimate {
0% {
transform: translateY(0);
}
90% {
transform: translateY(0);
}
100% {
transform: translateY(-142px);
}
}
@keyframes slideAnimate1 {
from {
transform: translateY(0);
}
to {
transform: translateY(-142px);
}
}
但是这个方法需要去计算百分比,会有一点麻烦。
- 那么接下来,我们直接监听
webkitAnimationIteration
事件,去实现相应的逻辑 - 最后,鼠标移入暂停事件应该如何实现呢?
我们可以利用animation
的animation-play-state
属性来控制。
以下就是完整代码:
<template>
<div class="task">
<div
:class="['move', { pause }]"
@mouseover="pauseAni"
@mouseout="playAni"
ref="moveRow"
>
<div
class="task_item"
v-for="(item, index) in data"
:key="index"
>{{ item }}</div>
</div>
</div>
</template>
<script>
import { reactive, toRefs, onMounted, nextTick, onUnmounted } from "vue";
export default {
name: 'animateTest',
setup() {
const state = reactive({
data: [1, 2, 3, 4, 5, 6],
// 运动的元素DOM
moveRow: null,
// 是否暂停动画
pause: false,
});
const animateEvent = () => {
// 基本逻辑
const newList = JSON.parse(JSON.stringify(state.data));
newList.push(newList.shift());
state.data = newList;
}
const playAni = () => {
// 判断一个数字,根据需求而定
if (state.data.length > 2) {
state.pause = false;
// 监听webkitAnimationIteration事件
state.moveRow.addEventListener('webkitAnimationIteration', animateEvent);
}
};
const initAnimate = async () => {
await nextTick();
playAni();
};
const pauseAni = () => {
state.pause = true;
};
onMounted(() => {
initAnimate();
console.log(1)
});
onUnmounted(() => {
state.moveRow && state.moveRow.removeEventListener('webkitAnimationIteration', animateEvent)
})
return {
...toRefs(state),
pauseAni,
playAni,
};
},
};
</script>
<style lang="scss" scoped>
.task {
position: relative;
width: 488px;
height: 284px;
padding: 2px 25px 3px 18px;
box-sizing: border-box;
margin-top: 4px;
background-color: rgb(213, 204, 204);
overflow: hidden;
.move {
animation: slideAnimate 5s linear infinite running;
/* 通过添加class来暂停动画 */
&.pause {
animation-play-state: paused;
}
}
&_item {
width: 100%;
height: 142px;
padding: 16px 0 17px 0;
border-bottom: 1px dashed rgba(35, 93, 105, 1);
color: #fff;
text-align: center;
line-height: 142px;
}
}
@keyframes slideAnimate {
0% {
transform: translateY(0);
}
90% {
transform: translateY(0);
}
100% {
transform: translateY(-142px);
}
}
</style>