工作原因,好久没更新博客了,今天来个用原生js写轮播图, 上代码
class Banner {
constructor(props = {
}) {
if (!(props?.root instanceof HTMLElement)) {
throw "root是必选dom节点";
}
// 状态
this.state = {
};
// 参数
this.props = {
type: "move",
height: 600,
width: 1000,
imgs: [],
...props,
};
if (props.type === "move") {
this.imgIndex = props.startIndex ? props.startIndex + 1 : 1;
} else {
this.imgIndex = props.startIndex || 0;
}
this.autoTimer; //自动轮播计时器
this.animateTimer; //动画计时器
this.init();
}
// 修改状态
setState = (state = {
}) => {
this.state = {
...this.state,
...state,
};
};
// 初始化
init = () => {
this.render();
this.animate();
this.autoMove();
this.onChange();
};
// 渲染轮播图
render = () => {
const {
root,
height,
width,
type,
imgs = [],
showButton = true,
showCheck = true,
} = this.props;
setStyle(root, {
height,
width,
position: "relative",
});
root.className += " banner";
const box = document.createElement("div");
const imgBox = document.createElement("ul");
const iconBox = document.createElement("ul");
imgs.forEach((item) => {
this.setImgNode(imgBox, item);
const icon = document.createElement("li");
iconBox.appendChild(icon);
});
// 滚动需要前后各插入图片
if (type === "move") {
this.setImgNode(imgBox, imgs[0]);
this.setImgNode(imgBox, imgBox.children[0], imgs[imgs.length - 1]);
} else {
setStyle(imgBox.children[this.imgIndex], {
zIndex: 90,
opacity: 1,
});
}
setStyle(imgBox, {
width: type === "move" ? (imgs.length + 2) * width : width,
height,
});
addClass(
box,
type === "move" ? "banner-img-box-move" : "banner-img-box-transparency"
);
iconBox.className = "banner-icon-box";
box.appendChild(imgBox);
root.appendChild(box);
showCheck && root.appendChild(iconBox);
showButton && this.renderButton(root);
this.setState({
box,
imgBox,
iconBox,
});
};
// 渲染按钮
renderButton = (node) => {
const rightBtn = document.createElement("button");
const leftBtn = document.createElement("button");
rightBtn.innerHTML = ">";
leftBtn.innerHTML = "<";
rightBtn.className = "btn banner-right-btn";
leftBtn.className = "btn banner-left-btn";
node.appendChild(rightBtn);
node.appendChild(leftBtn);
this.setState({
rightBtn,
leftBtn,
});
};
// 设置图片函数
setImgNode(node) {
const {
height, width, type } = this.props;
const li = document.createElement("li");
li.innerHTML = `${
arguments.length > 2 ? arguments[2] : arguments[1]
}" />`;
setStyle(li, {
height,
width,
opacity: type === "move" ? null : 0,
});
node.insertBefore(li, arguments.length > 2 ? arguments[1] || null : null);
}
//自动轮播
autoMove = () => {
const {
time = 3000 } = this.props;
this.autoTimer = setInterval(() => {
this.imgIndex++;
this.animate();
}, time);
};
//动画
animate = () => {
const {
type } = this.props;
const {
iconBox } = this.state;
//dom集合转数组
Array.from(iconBox.children).forEach((node, index) => {
if (node.className?.indexOf("active") > -1) {
removeClassName(node, "active");
}
if (this.imgIndex === (type === "move" ? index + 1 : index)) {
addClass(node, "active");
}
});
if (type === "move") {
this.move();
} else {
this.transparency();
}
};
//滚动轮播
move = () => {
const {
iconBox, box } = this.state;
const {
imgs, width } = this.props;
// 下标等于图片长度时需要重置到第一个小圆点
if (this.imgIndex === imgs.length + 1) {
addClass(iconBox.children[0], "active");
}
// 图片超过长度拉回第二张图片
if (this.imgIndex === imgs.length + 2) {
this.imgIndex = 2;
box.scrollLeft = width;
addClass(iconBox.children[1], "active");
}
if (this.imgIndex === 0) {
addClass(iconBox.children[imgs.length - 1], "active");
}
//下标小于0时返回最后二张图片的下标
if (this.imgIndex < 0) {
this.imgIndex = imgs.length - 1;
box.scrollLeft = width * imgs.length;
addClass(iconBox.children[imgs.length - 2], "active");
}
//滚动条移动
this.scrollMove();
};
//淡入淡出
transparency = () => {
const {
imgBox, iconBox } = this.state;
const {
imgs } = this.props;
if (this.imgIndex === imgs.length) {
//下标到最后一张图片时变回第一张图片
this.imgIndex = 0;
addClass(iconBox.children[0], "active");
}
if (this.imgIndex < 0) {
this.imgIndex = imgs.length - 1;
addClass(iconBox.children[imgs.length - 1], "active");
}
Array.from(imgBox.children).forEach((item, index) => {
this.opacitySwitch(item, 0);
item.style.zIndex = 0;
})
this.opacitySwitch(imgBox.children[this.imgIndex], 100);
imgBox.children[this.imgIndex].style.zIndex = 90;
};
// 所有操作函数
onChange = () => {
const {
iconBox } = this.state;
const {
root, type } = this.props;
iconBox.onmouseover = (e) => {
if (e.target.nodeName !== "LI") return;
clearInterval(this.autoTimer);
const index = [].indexOf.call(e.target.parentNode.children, e.target);
this.imgIndex = type === "move" ? index + 1 : index;
this.animate();
this.autoMove(); //重新启动
};
root.onclick = (e) => {
if (e.target.className?.indexOf("banner-right-btn") > -1) {
clearInterval(this.autoTimer);
this.imgIndex++;
this.animate();
this.autoMove();
} else if (e.target.className?.indexOf("banner-left-btn") > -1) {
clearInterval(this.autoTimer);
this.imgIndex--;
this.animate();
this.autoMove();
}
};
};
//透明度切换
opacitySwitch = (ele, target) => {
let num = 10;
clearInterval(this.animateTimer);
this.animateTimer = setInterval(() => {
let speed = target > num ? 5 : -5;
//剩余可运动量 <= 每次所走的量
if (Math.abs(target - num) <= Math.abs(speed)) {
clearInterval(this.animateTimer); //结束运动
ele.style.opacity = target / 100; //到达终点
} else {
num += speed;
ele.style.opacity = num / 100;
}
}, 30);
};
//滚动条移动
scrollMove() {
const {
box, imgBox } = this.state;
const {
width } = this.props;
clearInterval(this.animateTimer); //清除计算器
let minStep = 0; //起始步数
let maxStep = 20; //最大步数
let start = box.scrollLeft; //运动起始位置
let end = this.imgIndex * width; //结束位置
let everyStep = (end - start) / maxStep; //每一步的距离
this.animateTimer = setInterval(() => {
minStep++;
if (minStep >= maxStep) {
//判断到达最大步数
clearInterval(this.animateTimer); //清除计算器
}
start += everyStep; //起始位置加上走的距离
box.scrollLeft = start;
}, 20);
}
}
// 设置css
function setStyle(root, style = {
}) {
Object.keys(style).forEach(
(item) => (root.style[item] = getStyleValue(item, style[item]))
);
}
// 特殊处理设置宽高样式 数字
function getStyleValue(key = "", value) {
const arr = ["height", "width", "top", "left", "right", "bottom", "border"];
return arr.indexOf(key.toLowerCase()) > -1 &&
value !== 0 &&
value &&
!isNaN(value)
? value + "px"
: value;
}
// 增加类名
function addClass(ele, value) {
if (!ele.className) {
ele.className = value;
} else {
newClassName = ele.className;
newClassName += " ";
newClassName += value;
ele.className = newClassName;
}
}
// 删除class函数
function removeClassName(ele, className) {
let str = ele.className,
index = str.indexOf(className);
if (index > -1) {
ele.className = str.replace(className, "");
}
}
css 部分写的稍微简陋一些
* {
margin: 0;
padding: 0;
}
li {
list-style: none;
}
.banner {
position: relative;
}
.banner-img-box-move {
overflow: hidden;
height: 100%;
width: 100%;
position: relative;
}
.banner-img-box-move > ul > li > img {
height: 100%;
width: 100%;
}
.banner-img-box-move > ul > li {
float: left;
}
.banner-icon-box {
position: absolute;
right: 20px;
bottom: 20px;
z-index: 99;
display: flex;
}
.banner-icon-box > li {
height: 15px;
width: 15px;
margin-right: 12px;
background-color: #eee;
border-radius: 50%;
}
.banner-img-box-transparency {
position: relative;
}
.banner-img-box-transparency > ul > li {
position: absolute;
left: 0;
top: 0;
}
.banner-img-box-transparency> ul > li >img{
height: 100%;
width: 100%;
}
.btn {
position: absolute;
height: 40px;
width: 40px;
z-index: 99;
}
.banner-left-btn {
left: 0;
top: 50%;
transform: translateY(-50%);
}
.banner-right-btn {
right: 0;
top: 50%;
transform: translateY(-50%);
}
.active {
background-color: red !important;
}
使用
<script>
const imgs = ['images/1.jpg','images/2.jpg','images/3.jpg','images/4.jpg','images/5.jpg',]
const root = document.querySelector('#root');
new Banner({
imgs,
root,
type: 'transparency', //move是滚动轮播, transparency是透明度
width: 1000,
height: 400,
})
script>