开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第33天,点击查看活动详情、
轮播图是我们前端程序员必学的知识点之一,js轮播图有很多种,有简洁的,也有复杂的,越复杂越能考察我们掌握的js基础能力,面试和工作中使用原生写轮播图的场景都不多,但想要实现一个功能齐全的js轮播图还是有一点难度,下面我们来实现一个稍微复杂一点的。
- 首先来看实现以后的效果是什么:
介绍一下,主要功能点有这么几点
-
- 自动轮播功能
-
- 点击左侧和右侧的按钮 可以切换上一页下一页
-
- 点击下面的红色锚点,可以切换到指定图片
- 4.左上角区域有一个白色的字,那个是装饰,代码中大家能看到
那么从这些功能点当中能够考察我们的什么水平呢?
首先来看实现需求,实现上代码里没有标签,全靠
js
操作DOM
, 左侧和右侧的按钮要求使用canvas
绘制,要求图片在切换的时候有过渡效果,下面的红色锚点需要跟随当前选中图片切换样式,鼠标移入图片上的时候暂停轮播。
-
DOM
操作
-
canvas
基本能力
-
- 事件侦听操作
-
- 封装能力
-
JavaScript
基本功
下面我将会放代码,详细的操作我都写在了代码里面的注释当中,每个方法都有很详细的解释,大家可以放心食用~,那么图片地址的话,url
大家随便找几张自己本地的图片,把路径改改,代码一粘贴,就可以看到效果了,因为没有标签,所以只有 css
和 js
代码:
- css
<style>
body {
margin: 0;
padding: 0;
}
.carousel {
width: 100%;
height: 33.3vw;
min-height: 365px;
position: relative;
overflow: hidden;
}
.img-con {
width: 200%;
height: 100%;
min-width: 1200px;
position: absolute;
left: 0;
}
.img-con img {
width: 100%;
height: 100%;
}
.img-con .title {
position: absolute;
left: 15%;
top: 10%;
color: white;
font-size: 20px;
user-select: none;
}
.img-con .day {
font-size: 30px;
}
.img-con .image-item {
width: 50%;
height: 100%;
float: left;
position: relative;
}
.dot {
list-style: none;
position: absolute;
margin: 0;
padding: 0;
bottom: 30px;
left: 50%;
transform: translate(-50%, 0);
}
.dot a {
display: block;
width: 20px;
height: 20px;
border-radius: 20px;
border: 2px solid red;
transition: all 0.5s;
}
.dot>li {
margin-left: 10px;
float: left;
}
.dot>li:first-child {
margin-left: 0;
}
.left,
.right {
position: absolute;
top: 50%;
transform: translate(0, -50%);
}
.left {
left: 20px;
}
.right {
right: 20px;
}
</style>
- js
<script>
var list = [{
img: "./img/a.jpg",
date: "2/Nov.2021",
info: "食欲拯救计划|阿一古~是贼拉好吃的延吉啊!"
},
{
img: "./img/b.jpg",
date: "1/Nov.2021",
info: "徽州古村落赏秋自驾9日攻略!给这个秋天一个说走就走的理由!"
},
{
img: "./img/c.jpg",
date: "31/Oct.2021",
info: "致,荆棘鸟与淘金先驱者——三赴西澳自驾笔记"
},
{
img: "./img/d.jpg",
date: "30/Oct.2021",
info: "我是你的俘虏【跟新疆私奔】"
},
{
img: "./img/e.jpg",
date: "29/Oct.2021",
info: "遇见阳朔的光与影,晨与昏,山与水"
},
]
/**
* imgCon:获取图片所在的div
* dot:下面的小点
* prev:记录上一个下点的记录
* itemList:轮播的整个里面的每个 图片和文字的这一整个div以 0~4 存储
* bnList:轮播的下面的小圆圈存储
* pos:记录当前的轮播应该的下标位置
* direction:轮播向哪个方向滚动
* x:记录当前轮播所在的div的left
* moveBool:记录点击是否可以用
* speed:移动的速度
* autoBool:判断轮播是否可以自动轮播
* time:防抖
* LEFT:表示当前轮播向左方向移动
* RIGHT:表示当前轮播向右方向移动
*/
var imgCon, dot, prev;
var itemList = [],
bnList = [],
pos = 0,
direction = "left",
x = 0,
moveBool = false,
speed = 50,
autoBool = false,
time = 200;
const LEFT = Symbol(),
RIGHT = Symbol();
init();
/**
* 1.先创建一个div,用于存储整个轮播,再将轮播的className设置为carousel
* 2.创建轮播图片的DOM,包括里面的标题等
* 3.创建轮播下面的小点
* 4.创建左右按钮
* 5.将创建的轮播div添加到body里面
* 6.执行动画,让轮播可以自己滚动
* 7.设置最开始执行页面时,第一个小点可以添加背景颜色
* 8.给轮播div添加鼠标移入移出事件,让鼠标进入时,停止动画,离开时开始动画
*/
function init() {
var carousel = document.createElement("div");
carousel.className = "carousel";
createImageCon(carousel);
createDot(carousel);
createBnList(carousel);
document.body.appendChild(carousel);
animation();
changePrev();
carousel.addEventListener("mouseenter", mouseHandler);
carousel.addEventListener("mouseleave", mouseHandler);
}
/**
* 功能:鼠标滑入滑出轮播的侦听事件
* 1.设置是否自动播放autoBool的值
* 2.判断e.type是否是移入事件,是则赋值false,否则赋值true,并且将防抖的时间time设置为200
*/
function mouseHandler(e) {
autoBool = e.type === "mouseenter" ? false : (time = 200 && true);
}
/**
* 功能:创建小点
* 1.创建一个ul,然后设置ul的className为dot
* 2.然后根据list,使用reduce返回一个字符串,并将list的索引添加到创建的a标签中
* 3.将创建好的ul添加到carousel
* 4.侦听dot小点的点击事件
*/
function createDot(carousel) {
dot = document.createElement("ul");
dot.className = "dot";
dot.innerHTML = list.reduce((value, item, i) => {
return value += `<li><a href='#${i}'></a></li>`;
}, "");
carousel.appendChild(dot);
dot.addEventListener("click", dotClickHandler);
}
/**
* 创建轮播图片,然后将创建的添加到最大的轮播div中
* 1.创建一个装载第一个图片的div,设置className是img-con
* 2.向创建出来的轮播小div添加list中的第一个数据,然后执行getImageItem(0)
* 3.将创建好的div添加到carousel中
*/
function createImageCon(carousel) {
imgCon = document.createElement("div");
imgCon.className = "img-con";
imgCon.appendChild(getImageItem(0));
carousel.appendChild(imgCon);
}
/**
* 功能:创建左右按钮
* 1.循环两次分别创建
* 2.判断,执行createBn,传入一个布尔参数,真则创建了一个左边的按钮,否则创建了一个右边的按钮
* 3.根据i和0进行比较,真则设置按钮的className值为"left",否则“right”
* 4.将创建好的按钮添加到轮播中
* 5.将按钮添加到bnList里面
* 6.添加按钮的点击事件
*/
function createBnList(carousel) {
for (var i = 0; i < 2; i++) {
var bn = createBn(i === 0);
bn.className = i === 0 ? "left" : "right";
carousel.appendChild(bn);
bnList.push(bn);
bn.addEventListener("click", bnClickHandler);
}
}
/**
* 功能:创建一个canvas
* 1.创建一个新的canvas
* 2.设置canvas的宽高分别为30和60
* 3.设置canvas的背景颜色为白色半透明
* 4.设置canvas里面创建的是2d内容
* 5.填充canvas里面是纯白色
* 6.设置canvas的阴影
* 7.设置canvas的起点和终点,并将其连接
* 8.判断传入的参数是否为真,为真则表示创建左边的按钮画布,否则创建的是右边的,则将创建的左边的画布进行翻转
* 9.将创建好的canvas返回
*/
function createBn(left) {
var canvas = document.createElement("canvas");
canvas.width = 30;
canvas.height = 60;
canvas.style.backgroundColor = "rgba(0,0,0,0.4)";
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#FFF";
ctx.shadowBlur = 3;
ctx.shadowOffsetX = 2;
ctx.shadowOffsetY = 2;
ctx.shadowColor = "#666";
ctx.beginPath();
ctx.moveTo(10, 30);
ctx.lineTo(20, 15);
ctx.lineTo(20, 45);
ctx.closePath();
ctx.fill();
if (!left) canvas.style.transform = "scale(-1,1) translate(0,-50%)";
return canvas;
}
/**
* 功能:根据传入的参数,获取list的一条数据,将这条数据添加到div里面
* 1.判断itemList里面是否存在list[n],避免创建重复的div,如果itemList里面存在,则直接返回该元素
* 2.如果itemList里面没有,则先创建一个div,将该div的名字设置为image-item
* 3.然后根据参数在list里面获取的数据创建html文件,然后添加到新创建的div中,
* 4.将创建好的div添加到itemList里面,然后返回itemList[n]
*/
function getImageItem(n) {
if (itemList[n]) return itemList[n];
var div = document.createElement("div");
div.className = "image-item";
div.innerHTML = `<img src="${list[n].img}">
<div class='title'>
<div>${list[n].date.match(/(\d*)\/(.*)/).slice(1).reduce((value,item,i)=>{
if(i===0) value+=`<span class='day'>${item}</span>`;
else value+="/"+item;
return value;
},"")}</div>
<span>${list[n].info}</span>
</div>`
itemList[n] = div;
return itemList[n];
}
/**
* 功能:轮播小点的点击事件
* 1.判断当前点击的是否是小点里面的a标签,如果不是则直接跳出
* 2.获取当前点击的小点的索引
* 3.判断当前点击的小点是否大于全局的轮播图片pos,如果大于,则将LEFT的值赋值给direction,否则将RIGHT赋值
* 4.将当前点击的下标index的值赋值给pos
* 5.然后创建当前轮播图片的下一张图片createNextImg()函数
*/
function dotClickHandler(e) {
if (e.target.nodeName !== "A") return;
var index = Array.from(dot.children).indexOf(e.target.parentElement);
direction = index > pos ? LEFT : RIGHT;
pos = index;
createNextImg();
}
/**
* 功能:左右按钮的点击事件
* 1.如果当前点击的是左按钮,则让当前的pos--,如果pos小于0,则又将列表的长度-1赋值给pos,让它从最后一个向前轮播
* 2.然后将direction设置为RIGHT
* 3.如果当前点击的是右按钮,则让当前的pos++,如果pos大于列表的最后一个下标,则又将0赋值给pos,让它从第一个向后轮播
* 4.然后将direction设置为LEFT
* 5.然后执行创建下一个轮播图片的函数
*/
function bnClickHandler(e) {
if (this.className === "left") {
pos--;
if (pos < 0) pos = list.length - 1;
direction = RIGHT;
} else {
pos++;
if (pos > list.length - 1) pos = 0;
direction = LEFT;
}
createNextImg();
}
/**
* 功能:当切换当前轮播图片执行的函数,创建一个新的轮播图片
* 1.如果当前direction是LEFT,则表示当前的轮播图片要向向左移动,则表示要在当前轮播图片 后面 根据当前pos去获取一个新的图片去添加在后面,然后
* 轮播图片可以先不做移动,现在轮播图片还是之前的轮播图片,下一张要显示的轮播图片在当前轮播图片的**后面**
* 2.如果当前direction是RIGHT,则表示当前的轮播图片要向右移动,则表示要在当前轮播图片 前面 根据pos去获取一个新的图片添加在当前轮播图片前面,
* 然后将整个轮播图片移动一张轮播图片的宽度大小,移动之后,当前轮播还是之前的轮播图片,下一张要显示的轮播图片在当前轮播图片的**前面**
* 3.然后将moveBool修改为true,表示当前可以移动轮播图片
* 4.改变轮播小点的样式
*/
function createNextImg() {
if (direction === LEFT) {
x = 0;
imgCon.appendChild(getImageItem(pos));
} else if (direction === RIGHT) {
x = -getImageItem(pos).offsetWidth;
imgCon.insertBefore(getImageItem(pos), imgCon.firstElementChild);
}
imgCon.style.left = x + "px";
moveBool = true;
changePrev();
}
/**
* 功能:改变上一个轮播小点的背景颜色
* 1.判断上一个轮播图是否存在,如果存在,则将该小点的背景设置为红色透明
* 2.将当前的轮播图的小点位置的小点赋值给prev
* 3.并将当前prev所代表的小点背景设置为红色不透明
*/
function changePrev() {
if (prev) {
prev.style.backgroundColor = "rgba(255,0,0,0)";
}
prev = dot.children[pos].firstElementChild;
prev.style.backgroundColor = "rgba(255,0,0,1)";
}
/**
* 功能:实现自动轮播
* 1.requestAnimationFrame(animation);
* 2.执行moveAnimation();函数
* 3.执行自动轮播函数
*/
function animation() {
requestAnimationFrame(animation);
moveAnimation();
autoMove();
}
/**
* 功能:移动轮播的动画
* 1.如果moveBool为false,表示不能移动当前轮播,跳出
* 2.如果direction是LEFT,则表示当前轮播图片要向左移动,则使控制轮播定位的left值x减去speed,如果x小于了-当前轮播的宽度,
* 则将当前轮播移除,然后将后面的轮播图移动到最前面,设置x=0,然后设置不能移动轮播
* 3.如果direction是RIGHT,则表示当前轮播图片要向右移动,则使控制轮播定位的left值x加上speed,如果x大于0,则将当前轮播移除,
* 然后将前面的轮播图移动到最后面,设置x=0,然后设置不能移动轮播
* 4.设置轮播图定位left为x
*/
function moveAnimation() {
if (!moveBool) return;
if (direction === LEFT) {
x -= speed;
if (x <= -imgCon.firstElementChild.offsetWidth) {
imgCon.firstElementChild.remove();
x = 0;
moveBool = false;
}
} else if (direction === RIGHT) {
x += speed;
if (x >= 0) {
imgCon.lastElementChild.remove();
x = 0;
moveBool = false;
}
}
imgCon.style.left = x + "px";
}
/**
* 功能:轮播图自动移动
* 1.判断是否可以自动轮播,由鼠标是否移入移出轮播决定
* 2.设置防抖的time--,然后判断time是否大于0,则跳出,否则将time重新设置为200
* 3.让右边的按钮自动抛发一个点击的事件
*/
function autoMove() {
if (!autoBool) return;
time--;
if (time > 0) return;
time = 200;
bnList[1].dispatchEvent(new MouseEvent("click"));
}
</script>