原生js编写3D版轮播图
在日常开发中,为了开发的快捷性基本上都是把UI组件中的组件拿过来用即可,但是有时可能无法实现我们的需求,这时我们就需要去造轮子。
今天我们用原生的js和css3以及html来编写一个3D轮播图,如下:
第一步,编写轮播图的大致结构
使用html把创建轮播图结构
<div id="box">
<!--
创建轮播图中图片的容器,注意图片的存放位置
-->
<div class="item item1">
<img src="./1.gif" class="swipe_item1">
</div>
<div class="item item2">
<img src="./2.gif" class="swipe_item2">
</div>
<div class="item item3">
<img src="./3.gif" class="swipe_item3">
</div>
<div class="item item4">
<img src="./4.gif" class="swipe_item4">
</div>
<div class="item item5">
<img src="./5.gif" class="swipe_item5">
</div>
<div class="item item6">
<img src="./6.gif" class="swipe_item6">
</div>
<div class="item item7">
<img src="./7.gif" class="swipe_item7">
</div>
<!--
创建轮播图切换的按钮,由于我们的轮播图中没有设置点击图片切换的功能,所以使用一个div覆盖在了轮播图之上
-->
<div id="button" style="position: absolute;top: 0;width: 100%;z-index: 999;">
<button type="button" onclick="left(event)" class="left switch">左</button>
<button type="button" onclick="right(event)" class="right switch">右</button>
</div>
</div>
使用css将轮播图结构的排列样式优化好
/*设置整个轮播图水平居中*/
body{
display: flex;
justify-content: center;
align-items: center;
}
/*预防按钮点击时出现丑陋的边框*/
*{
outline: none;
}
#box{
margin-top: 300px;
width: 1350px;
height: auto;
position: relative;
}
/*设置定位方式,和垂直居中*/
.item{
position: absolute;
top: 50%;
transform: translateY(-50%);
}
/*设置每个图片容器的位置:横向位置、z轴位置*/
.item1{
left: 0px;
z-index: 9;
}
.item2{
left: 100px;
z-index: 18;
}
.item3{
left: 250px;
z-index: 27;
}
.item4{
left: 450px;
z-index: 36;
}
.item5{
right: 250px;
z-index: 27;
}
.item6{
right: 100px;
z-index: 18;
}
.item7{
right: 0;
z-index: 9;
}
/*设置图片的宽度*/
.swipe_item1{
width: 300px;
}
.swipe_item2{
width: 350px;
}
.swipe_item3{
width: 400px;
}
.swipe_item4{
width: 450px;
}
.swipe_item5{
width: 400px;
}
.swipe_item6{
width: 350px;
}
.swipe_item7{
width: 300px;
}
/*为图片设置阴影,更好的凸显3D效果*/
img{
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 10px;
}
/*设置切换按钮的位置,及样式*/
.left{
left: 100px;
}
.right{
right: 100px;
}
.switch{
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 50px;
height: 50px;
border-radius: 50%;
border: none;
background-color: rgba(0,0,0,0.4);
color: white;
}
.switch:hover{
background-color: rgba(0,0,0,0.7);
}
至此轮播图的基本结构已经完成,此时的效果如下:
我们总结一下上面的主要代码:
- 为图片容器设置绝对(absolute)定位
- 设置图片容器垂直居中
- 根据绝对定位为图片容器设置所处位置(横向)和z轴位置
- 切换按钮始终悬浮
第二步,使用css3写切换动画
我们以最突出的图片class="item4"为例,来编写动画。
首先,确定to和from,即动画中的“起点”和“终点”,把item4移动到item3的位置上。确定需要变化的属性:
- 图片的width
- 容器大width
- 容器的位置(left)
查看item3和item4的css代码,如下:
/*容器*/
.item3{
left: 250px;
z-index: 27;
}
.item4{
left: 450px;
z-index: 36;
}
/*图片*/
.swipe_item3{
width: 400px;
}
.swipe_item4{
width: 450px;
}
因此动画css代码如下:
.animation4 {
animation: item4_animation 1s;
animation-fill-mode: forwards; /*设置动画结束后,元素停在终点*/
}
.a4 {
animation: item4_animation_index 1s;
animation-fill-mode: forwards;
}
@keyframes item4_animation {
from {
width: 450px;
}
to {
width: 400px;
}
}
@keyframes item4_animation_index {
from {
width: 450px;
left: 450px;
}
to {
width: 400px;
left: 250px;
}
}
第三步,改变元素的z-index属性
在第二步中,我们改变了容器和图片的位置和大小,但是z-index并没有改变:我们使用js来改变z-index
document.getElementsClassName("item4").style.zIndex = 27
至此,我们已经将item4通过动画的方式移动到了item3的位置上,效果如下:
问题:
- 移动后的item4覆盖了item3
- 如果我们下次再移动item4,item4会自动跳到的初始位置,然后再开始动画
上面的两个问题如何解决呢?
第一个问题,轮播图的最终效果是所有图片一起移动,因此不会出现覆盖问题
第二个问题,我们可以在动画结束之后,通过js把class=“item4”变成class=“item3”(由于我们现在向左移动)
以上都是以item4这一个元素的移动来进行分析
第四步,分析
- 使用css为每一个容器的移动,编写动画代码,动画分为左和右
- 使用js来改变移动后的容器的 “z-index” 属性
- 使用js来切换对应的class,并删除当前的class,根据向左和向右改变class,例:
向左移动:item4 => item3 | item1 => item7
向右移动:item4 => item5 | item7 => item1
第五步,js动态设置切换
我们以代码为例:
let flag = true
let autoFlag = true
function left(e){
//只有上一个动画结束后,才能继续切换
if(!flag){
return
}
flag = false
//获取页面中所有的item,即所有的图片容器
let arr = document.getElementsByClassName("item")
for(let i=1;i<=arr.length;i++){ //由于没有item0,所以从1开始
let item = document.getElementsByClassName("item"+i)[0] //通过("item"+i)拼接得到对应的class,如item1
let swipe_item = document.getElementsByClassName("swipe_item"+i)[0]//同理可得swipe_item1
swipe_item.classList.add("animation"+i) //为图片添加width改变动画
item.classList.add("a"+i) //为容器添加width和位移改变动画
//如下:根据不同i,实际是不同的("item"+i),来设置对应的z-index
setTimeout(()=>{
switch(i){
case 7:
item.style.zIndex = 18
break
case 6:
item.style.zIndex = 27
break
case 5:
item.style.zIndex = 36
break
case 4:
item.style.zIndex = 27
break
case 3:
item.style.zIndex = 18
break
case 2:
item.style.zIndex = 9
break
}
},500) //时间可变,根据需求
//当动画加载完成后,我们来切换class
setTimeout(()=>{
//注意:在切换class时,一定要配合 animation-fill-mode: forwards; 使用
//我们先把图片和容器的动画class和原始class删除
swipe_item.classList.remove("animation"+i)
swipe_item.classList.remove("swipe_item"+i)
item.classList.remove("a"+i)
item.classList.remove("item"+i)
//由于是向左移动,item1要移动到item7的位置上,所以item1需要特殊处理
if(i != 1){
swipe_item.classList.add("swipe_item"+(i-1))
item.classList.add("item"+(i-1))
}else{
//如果i = 1 时,该容器(移动的容器和图片)添加swipe_item7和item7到classList中
swipe_item.classList.add("swipe_item"+7)
item.classList.add("item"+7)
}
flag = true
},1000) //对应动画执行的时间
}
}
至此我们便可以随意向左切换:
向右移动的方式参考向左移动的方法,需要注意z-index的设置,和切换class,代码如下:
function right(e){
if(!flag){
return
}
flag = false
let arr = document.getElementsByClassName("item")
for(let i=1;i<=arr.length;i++){
let item = document.getElementsByClassName("item"+i)[0]
let swipe_item = document.getElementsByClassName("swipe_item"+i)[0]
swipe_item.classList.add("r_animation"+i)
item.classList.add("r_a"+i)
setTimeout(()=>{
switch(i){
case 6:
item.style.zIndex = 9
break
case 5:
item.style.zIndex = 18
break
case 4:
item.style.zIndex = 27
break
case 3:
item.style.zIndex = 36
break
case 2:
item.style.zIndex = 27
break
case 1:
item.style.zIndex = 18
break
}
},500)
setTimeout(()=>{
swipe_item.classList.remove("r_animation"+i)
swipe_item.classList.remove("swipe_item"+i)
item.classList.remove("r_a"+i)
item.classList.remove("item"+i)
if(i != 7){
swipe_item.classList.add("swipe_item"+(i+1))
item.classList.add("item"+(i+1))
}else{
swipe_item.classList.add("swipe_item"+1)
item.classList.add("item"+1)
}
flag = true
},1000)
}
}
自动滚动和鼠标事件:
document.getElementById("box").onmouseover = ()=>{
autoFlag = false
}
document.getElementById("box").onmouseout = ()=>{
autoFlag = true
}
setInterval(()=>{
if(autoFlag){
right()
}
},5000)
至此,我们所有的功能都以实现
源码
3D轮播图的源码:(css、js、html)
源码链接