无缝连接 js轮播图 插件
初衷
实习考核的时候,按照要求要用vue封装一个轮播图插件, 因为自己之前有过用原生js写过轮播图的经验, 本以为能顺利实现, 但是还是遇到了问题 —— 无缝连接。然后自己网上各种的查找无缝连接的实现原理, 都大同小异, 然而当自己去用代码实现的时候, 却不能达到想要的效果, 就这样查查改改实现下来, 发现了** 关键问题 ** 所在, 于是整理了自己的思路, 用 ** 原生js ** 实现一下。
效果
原理
两个关键点
-
DOM 结构
- 末尾重复第一张, 开头重复最后一张图
-
临界处理
-
向右滚动到第 1 张(最右边)的时候, **取消过渡效果 *, ** 并迅速复原位置 ** (最左边第 1 张) * 见代码
-
向左滚动到第 3 张(最左边)的时候, **取消过渡效果, ** 并迅速复原位置 ** (最右边第 3 张)
-
功能
-
无缝连接, 自动播放
-
循环播放设置
-
过渡动画设置
-
左右箭头切换下一张
-
指示器切换
代码
html
<!-- 因为写的是插件, dom 结构是js动态添加, 这里只是展示添加过后的主要结构 -->
<div id="carousel-wrapper">
<ul class="carousel__list">
<li><img src="./images//carousel_03.webp" alt="" /></li>
<li><img src="./images//carousel_01.webp" alt="" /></li>
<li><img src="./images//carousel_02.webp" alt="" /></li>
<li><img src="./images//carousel_03.webp" alt="" /></li>
<li><img src="./images//carousel_01.webp" alt="" /></li>
</ul>
</div>
js
- 创建Carousel轮播图对象
function Carousel(el, option) {
this.el = el; // 挂在dom 对象
this.images = option.images; // 图片数组
// ?? 与 || 运算符相同, 区别是如果 ?? 前值是false也能取到, ||取不到
this.autoPlay = option.autoPlay ?? true; // 是否自动播放
this.loop = option.loop ?? true; // 是否循环播放
this.easing = option.easing || "ease"; // 动画效果
this.list = null; // 轮播图对象
this.indic = null; // 指示器wrapper
this.indics = []; // 指示器
this.len = this.images.length;
this.elWdith = this.el.offsetWidth; // 轮播图宽度
this.timer = null;
this.currentIndex = 1; // 当前显示图片索引
this.init();
}
-
初始化
dom结构初始化和事件绑定初始化
Carousel.prototype.init = function () {
// 初始化dom 结构
this.initDom();
// 初始化事件绑定
this.initEvent();
// 是否自动播放
this.autoPlay && this.autoMove();
};
-
*移动函数 (重点) *
使用setTimeout,延迟代码块执行,** 延迟执行的时间必须和过渡的时间相同 **
后面的切换事件的** 公用函数 **
Carousel.prototype.move = function () {
// 右临界值处理
if (this.currentIndex >= this.len + 1) {
/* 当 this.currentIndex = 4 时, 此时不会立即执行这块代码,复原位置,
而是执行最下面的代码, 滚动到第 1 张(最后的 1 ) 时,才执行, 因为没有了过渡, 用户看不到真正滚动了, 其实这时候已经已经到第 1 张 ( 开始的 1 ) */
setTimeout(
function () {
this.list.style.transition = null; // 清除过渡效果
this.currentIndex = 1; // 重置索引
this.list.style.transform = `translateX(${-this.elWdith}px)`; // 重置位置
}.bind(this),
1000
);
}
// 左临界值处理
if (this.currentIndex <= 0) {
setTimeout(
function () {
this.list.style.transition = null;
this.currentIndex = this.len;
this.list.style.transform = `translateX(${-this.elWdith * this.len}px)`;
}.bind(this),
1000
);
}
// 先执行此处代码
this.list.style.transition = `all 1s ${this.easing}`; // 开启过渡效果
this.list.style.transform = `translateX(${
-this.currentIndex * this.elWdith
}px)`;
};
- 自动移动播放
//自动移动
Carousel.prototype.autoMove = function () {
const that = this;
this.timer = setInterval(function () {
that.currentIndex++;
that.move();
}, 2000);
};
- 其他方法
// 切换上一张
Carousel.prototype.pre = function () {
this.currentIndex--;
this.move();
};
// 切换下一张
Carousel.prototype.next = function () {
this.currentIndex++;
this.move();
};
// 暂停播放
Carousel.prototype.pausePlay = function () {
clearInterval(this.timer);
};
Carousel.prototype.initDom // 初始化dom 结构
Carousel.prototype.initEvent // 事件绑定
bug 解决
滚动到临界值处, 在过渡时间内点击切换按钮, 以及短时间内连续点击切换, 会出现闪动或者轮播卡顿。使用节流函数优化点击事件
/**
* 节流函数
* @param { Number } wait 等待多长执行 尽量和过渡动画时间接近
* @param { Function } fn 回调函数
* @return {}
*/
let pre = 0; // 开始时间
const throttle = function (wait, fn) {
return function () {
const context = this;
let args = arguments;
const now = Date.now(); // 返回自 1970 年 1 月 1 日 00:00:00 到当前时间的毫秒数。
if (now - pre > wait) {
fn.call(context, args);
pre = now;
}
};
};
插件使用
<!-- 1. 引入 样式文件 -->
<link rel="stylesheet" href="./style.css" />
<!-- 2. 引入 js -->
<script src="./index.js"></script>
var wrapper = document.getElementById("carousel-wrapper");
// 3. 实例化Carousel对象, 传入参数
/*
* 第一个参数为挂载的dom对象, 必须传
* 第二个参数: 配置对象{}
* images: 轮播的图片数组, 每张图片是一个对象, 必须包含src 属性
* autoPlay: 是否自动播放, 默认true
* arrow: 是否显示箭头切换按钮, 默认true
* indicator: 是否显示指示器, 默认true
* easeing: 过度效果, 默认为 ease
*/
new Carousel(wrapper, {
images: [
{ src: "./images//carousel_01.webp", alt: "" },
{ src: "./images//carousel_02.webp", alt: "" },
{ src: "./images//carousel_03.webp", alt: "" },
],
easing: "ease",
loop: true,
autoPlay: true,
});
写在最后
根据自己的思路去实现出来, 发现问题, 解决问题, 对自己是一个很大的提升。 需要完整源码的可以私我, 感谢大家的支持。发现问题请及时向我提出。