animationend 事件在 CSS 动画完成后触发。
CSS 动画播放时,会发生以下三个事件:
- animationstart - CSS 动画开始后触发
- animationiteration - CSS 动画重复播放时触发
- animationend - CSS 动画完成后触发
在这三个事件函数中,均可以使用 event.animationName 属性,判断是在执行哪个动画。
个人觉得 animationend 事件用的多一点。
案例1:标签的隐现
<button type="button" id="btn">点击</button>
<div class="box" id="box"></div>
.box{
width: 100px;
height: 100px;
background: #f00;
display: none;
margin-left: auto;
margin-right: auto;
}
.box.show{
display: block;
}
@keyframes showAni {
0%{
opacity: 0;
}
100%{
opacity: 1;
}
}
@keyframes hideAni {
0%{
opacity: 1;
}
100%{
opacity: 0;
}
}
- 默认 div.box 是隐藏的。
- 点击按钮,让 div.box 显示;但是动画效果全部由 CSS 的 keyframes 去完成。
- 显示的动画很简单,让 div.box 显示的同时,执行 animation 动画就行。显示利用了 类 show 去执行,好处是可以判断 div.box 是执行显示效果,还是执行隐藏效果。
- 但是隐藏的时候,要先执行动画,当动画完毕的时候,再利用 animationend 事件函数,让 div.box 隐藏。
let btn = document.getElementById("btn");
let box = document.getElementById("box");
btn.addEventListener("click",function(){
if( !box.classList.contains("show") ){ // 如果 box 不包含 类show,说明需要显示它。
box.classList.add("show");
box.style.animation = "showAni 0.5s both";
}else{ // 否则就要隐藏
box.style.animation = "hideAni 0.5s both";
}
});
// 隐藏动画完毕时,就要去掉 show 的类。
box.addEventListener("animationend",function(event){
if(event.animationName === "hideAni" ){
box.classList.remove("show");
}
});
案例2:多个标签的隐现
<div style="text-align:center;">
多个标签的动画显示和隐藏
<button type="button" id="btn">点击</button>
</div>
<div class="box" id="box">
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</div>
*{
margin: 0;
padding: 0;
}
ul,li,ol{
list-style-type: none;
}
.box{
width: 100px;
height: 100px;
display: none;
perspective: 500px;
margin-left: auto;
margin-right: auto;
}
.box li{
background: #f00;
}
.box.show{
display: block;
}
@keyframes showAni {
0%{
opacity: 0;
transform: rotateY(90deg);
}
100%{
opacity: 1;
transform: rotateY(0deg);
}
}
@keyframes hideAni {
0%{
opacity: 1;
transform: rotateY(0deg);
}
100%{
opacity: 0;
transform: rotateY(90deg);
}
}
整体思路,跟前面的很类似。
不过,这次的动画是直接写在 每个 li 上面,而不是 div.box 整体。因此,显示的时候,要对 li 遍历,依次开展动画;隐藏的时候,也是同理。
在多个标签隐藏时候,要用到 setTimeout,等待所有动画执行完毕后,才去掉 div.box 的类 show。
let btn = document.getElementById("btn");
let box = document.getElementById("box");
btn.addEventListener("click",function(){
let li = box.querySelectorAll("li"); // 获取所有的 li 标签
if( !box.classList.contains("show") ){
box.classList.add("show");
li.forEach(function(value, key, parent){
value.style.animation =`showAni 0.2s ${0.2*key}s both`;
});
}else{
// for(let i=li.length-1; i>=0 ; i--){
// li[i].style.animation =`hideAni 0.2s ${0.2*(li.length-1-i)}s both`;
// }
li.forEach(function(value, key, parent){
value.style.animation =`hideAni 0.2s ${0.2*(li.length-1-key)}s both`;
});
setTimeout(function(){
box.classList.remove("show");
},200*(li.length-1)); // 等待所有动画执行完毕,去掉 box 的show类。
}
});
案例3:图片轮播
这个效果应该被写滥了。。。。几乎是一个学 JS 的人必会。
不过,这次我结合 animationend 事件写一个,带有渐隐效果的轮播。以前都是用 jQuery,今天换个思路。
HTML 代码如下:
<div class="picShow">
<ul class="picUl" id="picUl">
<li><a href="#"><img src="../images/pic1.jpg" alt=""></a></li>
<li><a href="#"><img src="../images/pic2.jpg" alt=""></a></li>
<li><a href="#"><img src="../images/pic3.jpg" alt=""></a></li>
<li><a href="#"><img src="../images/pic4.jpg" alt=""></a></li>
<li><a href="#"><img src="../images/pic5.jpg" alt=""></a></li>
</ul>
<div class="picDots" id="picDots"></div>
</div>
- 轮播的控制点利用 DOM,根据图片的个数( li 的个数)动态生成;
- 默认图片是隐藏的,display:none;
- li 的显示和隐藏是这个案例的关键,因此,把标签的隐藏和显示分别封装成了 函数。其中,为了过渡隐藏动画,利用了 animationend 事件。当隐藏动画完毕后,让标签 display:none;
CSS代码如下:
*{
margin: 0;
padding: 0;
}
img{
border:none;
}
ul,li,ol{
list-style: none;
}
.picShow{
position: relative;
margin-left: auto;
margin-right: auto;
}
.picShow,
.picUl>li,
.picUl>li>a{
width: 600px;
height: 399px;
overflow: hidden;
}
.picUl>li>a{
display: block;
}
.picUl>li{
position: absolute;
top:0;
left:0;
display: none;
}
.picUl>li.show{
display: block;
}
.picDots {
position: absolute;
right:20px;
bottom:20px;
}
.picDots span{
display: inline-block;
width: 16px;
height: 16px;
background: #fff;
margin-left: 10px;
cursor: pointer;
}
.picDots span.on{
background: #f30;
}
@keyframes showAni {
0%{
opacity: 0;
}
100%{
opacity: 1;
}
}
@keyframes hideAni {
0%{
opacity: 1;
}
100%{
opacity: 0;
}
}
JavaScript 代码如下:
let li = document.querySelectorAll("#picUl>li");
let dots = document.getElementById("picDots");
let span = [];
function init(){
for(let i=0; i<=li.length-1 ; i++ ){
// 动态生成轮播的控制点
let newSpan = document.createElement("span");
dots.appendChild(newSpan);
span.push(newSpan);
if( i===0 ){
newSpan.className = "on";
}
// 给每个 li 添加 animationend 事件
li[i].addEventListener("animationend",function(event){
if( event.animationName === "hideAni"){
this.classList.remove("show");
}
});
}
showTag(li[0]);
}
// 显示某个标签
function showTag(tag){
tag.classList.add("show");
tag.style.animation = `showAni 0.5s both`;
}
// 隐藏某个标签
function hideTag(tag){
tag.style.animation = `hideAni 0.5s both`;
}
init(); // 初始化函数
// 给 span 添加事件
for(let i=0 ; i<= span.length-1; i++){
span[i].addEventListener("click",function(){
this.classList.add("on");
showTag(li[i]);
// 其余的点去掉 on,其余的 li 隐藏。
for(let j=0 ; j<=span.length-1 ; j++ ){
if( j!==i){
span[j].classList.remove("on");
hideTag(li[j]);
}
}
});
}
案例4:带退出动画的轮播
具体效果可以参考网游逆水寒的官网。
在人物轮播中,旧的内容动画退出后,才出现新的人物。
借用前面的思路,我也来做一个简单的带退出动画的轮播。
样式写的简陋,但是思路很重要。
HTML结构:
<div class="box">
<ul class="picUl" id="picUl">
<li>
<img src="../images/1.jpg" alt="">
<h1>图片1</h1>
</li>
<li>
<img src="../images/2.jpg" alt="">
<h1>图片2</h1>
</li>
<li>
<img src="../images/3.jpg" alt="">
<h1>图片3</h1>
</li>
</ul>
<div class="dots" id="dots"></div>
</div>
CSS:要在CSS 里写出每个部分的进入、退出动画。
*{
margin: 0;
padding: 0;
}
ul,li,ol{
list-style: none;
}
img{
border:none;
}
.picUl img{
width: 200px;
}
.box{
background: #ff0;
margin-left: auto;
margin-right: auto;
position: relative;
overflow: hidden;
}
.box,
.picUl>li{
width: 400px;
height: 200px;
overflow: hidden;
}
.picUl>li img{
position: absolute;
left:0;
top:30px;
}
.picUl>li h1{
position: absolute;
right:0;
top:30px;
}
.picUl>li{
display: none;
}
.picUl .show{
display: block;
}
@keyframes leftIn {
0%{
opacity: 0;
transform: translateX(-100px);
}
100%{
opacity: 1;
transform: translateX(0);
}
}
@keyframes leftOut {
0%{
opacity: 1;
transform: translateX(0);
}
100%{
opacity: 0;
transform: translateX(-100px);
}
}
@keyframes rightIn {
0%{
opacity: 0;
transform: translateX(100px);
}
100%{
opacity: 1;
transform: translateX(0);
}
}
@keyframes rightOut {
0%{
opacity: 1;
transform: translateX(0);
}
100%{
opacity: 0;
transform: translateX(100px);
}
}
.dots{
position: absolute;
left:0;
right:0;
bottom:10px;
text-align: center;
}
.dots span{
display: inline-block;
width: 16px;
height: 16px;
background: #fff;
margin-left: 5px;
margin-right: 5px;
cursor: pointer;
}
.dots span.on{
background: #f00;
}
JavaScript:
let dots = document.getElementById("dots");
let li = document.querySelectorAll("#picUl>li");
let img = document.querySelectorAll("#picUl img");
let nowIndex = 0;
let nextIndex = 0 ;
let dotSpan = [];
function init(){
for(let i=0 ; i<=li.length-1 ; i++){
let newSpan = document.createElement("span");
dots.appendChild(newSpan);
dotSpan.push( newSpan );
if( i === 0 ){
newSpan.classList.add("on");
}
}
showTag( li[0] );
}
function showTag(tag){
tag.classList.add("show");
tag.children[0].style.animation = "leftIn 0.3s both";
tag.children[1].style.animation = "rightIn 0.3s both";
}
function hideTag(tag){
tag.children[0].style.animation = "leftOut 0.3s both";
tag.children[1].style.animation = "rightOut 0.3s both";
}
init(); // 初始化init
// 给图片添加动画事件。当图片动画结束后,让 li 隐藏
// 给控制点添加事件
for(let i=0 ; i <= li.length-1 ; i++ ){
img[i].addEventListener("animationend",function(event){
if( event.animationName === "leftOut" ){
this.parentNode.classList.remove("show");
}
});
dotSpan[i].addEventListener("click",function(){
this.classList.add("on");
for( let j=0 ; j <= li.length-1 ; j++ ){
if( j !== i ){
dotSpan[j].classList.remove("on");
hideTag( li[j] )
}
}
// 待退出后,再执行进入动画;
// 不写 setTimeout ,直接 运行 showTag
// 则是退出和进入同时进行
setTimeout(function(){
showTag( li[i] );
},300);
});
}
瞬间觉得轮播太有意思了~~