上一章发的都是代码,思路没有写出来,这一章主要是将思路,怎么写的。当然代码在最后哦!图片绝对能滚起来哦。
无缝轮播图在日常生活中随处可见,比如在我们csdn的首页就有无缝轮播图,所以我讲解一下我对无缝轮播图的见解和思路。
- 首先我是从整体分析了一下,我们可以先看一下无缝轮播图的结构样式。其中含有图片若干张,左右箭头,下方指示器。我是先写一个div用来包含所有的元素。图片单独用一个div,左右箭头,指示器都是单独用一个div包含。只不过我考虑到以后工作场景,所以我的图片和指示器都没有在html中显示,只是先写一个看一下样式用于前期的网页测试。
- 然后我开始写css样式,css样式我是首先用到了定位,全部基于第一个写的div定位,然后溢出隐藏。
给图片增加样式,用width的值用%号来写,高度也是这样。一张图片就是占满一个大的div。有几张图片就几个100%。左右箭头都是使用的矢量图标,所以可以font-size调试大小,在调试位置。最后是指示器的相关样式,指示器是行快盒,也可以用浮动来使指示器都在一行上显示。
最后在给指示器增加一个显示第几张就其相对应的指示器显示颜色,即激活指示器。- 然后静态部分写好了。
开始分析如何使图片无缝轮播
顾名思义,无缝轮播,就是必须要有计时器,图片之间的动画不能有缝隙。动画过程中流畅,图片切换自如 。
所以必须要写动画,我是抽离出来了动画的部分,最后直接调用即可。我是先定义了一个函数,用于在别的部分调用这个动画函数,完成动画操作。动画嘛,理所当然就会有起始值,结束值,变化的总时间,动画间隔的时间,和动画在间隔时间的变化次数(变化次数最好要返回整数,小数的画会出现一些不必要的麻烦),然后是动画的每一次的变化改变的值,然后还有变化的次数,函数最后再写个计时器,也是这个动画函数的核心部分,动画首先都是起始值加上上一次变化的值,然后变化的次数在增加一次,做一下判断,变化的次数是不是达到了变化的总次数,然后表示变化完成了,就清除这个计时器,表示动画完成了,不需要变化了,然后调用一下表示动画开始的函数和动画结束的函数。最后在写一个无数的可能性,就是表示动画开始的函数和传入一个参数from。用于调用动画开始。- 最后就是js的基础部分。
写js基础部分的时候,我是先思考一下,要怎么开始写,从哪里开始下手写。
我考虑到图片路径,(为了以便更好的贴合实际工作需求)所以我是把图片放在一个数组中了,以便写指示器和图片的相关操作。
我要先定义一个全局变量表示当前显示的是第几张图片,索引操作。然后开始写一个函数,用于创建图片到容器中,在设置一个容器div的宽度,因为包裹图片的容器,宽度取决于内部图片的数量,然后再创建指示器,根据我定义的那个全局变量(表示当前显示的是第几张图片)来设置指示器的激活(变色)状态。
然后再写一个函数,在函数中定义一个我要切换到图片索引的变量,再计算容器的marginLeft,将容器从当前的marfinLeft变化到新的marginLeft,
(为什么要用marginLeft纳?因为图片是这个容器中的一部分,而css中把图片的大小等于了n00%,所以可以用这个)
再更新全局变量(表示当前显示的是第几张图片)的值,就是等于我要切换到的图片索引的变量,最后再更改一个指示器的状态。
然后再写两个函数,表示向前切换一张图片,向后切换一张图片,就是无缝切换到下一张或者上一张图片,然后调用我写的那个表示切换图片的函数,然后再向下一张图片切换的函数中最后写道最后一张图片切换完成后要回归到第一张,相反,向上切换一张就是从第一张图片开始切换,先跳到最后一张的图片,再进行无缝切换。
最后开始鼠标点击事件和移动事件,首先是左右箭头写事件,再写开始动画无缝轮播,停止动画无缝轮播,然后写鼠标移动事件,移动到图片上去停止动画轮播,移开开始播放动画。
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>无缝轮播图title>
<link rel="stylesheet" href="./index.css">
<link rel="stylesheet" href="//at.alicdn.com/t/font_3281875_e4prwerl5ms.css">
head>
<body>
<div class="carousel-container">
<div class="carousel-list">
div>
<div class="arrow-left">
<i class="iconfont icon-lunbotujiantou-">i>
div>
<div class="arrow-right">
<i class="iconfont icon-lunbotujiantou-1">i>
div>
<div class="indicator">
div>
div>
<script src="./js/animate.js">script>
<script src="./js/index.js">script>
body>
html>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.carousel-container {
position: relative;
width: 500px;
height: 300px;
/* outline: 1px solid; */
margin: 0 auto;
margin-top: 20px;
overflow: hidden;
}
/* 图片的相关样式 */
.carousel-list {
width: 500%;
height: 100%;
}
.carousel-item {
width: 500px;
height: 100%;
object-fit: cover;
}
/* 左右箭头相关样式及背景样式 */
.arrow-left,
.arrow-right {
position: absolute;
background-color: rgba(0, 0, 0, 0.2);
width: 50px;
height: 100%;
top: 0;
color: #fff;
text-align: center;
line-height: 300px;
}
.arrow-left:hover,
.arrow-right:hover {
background-color: rgba(0, 0, 0, 0.5);
}
.arrow-left i {
cursor: pointer;
}
.arrow-right i {
cursor: pointer;
}
.arrow-left {
left: 0;
}
.arrow-right {
right: 0;
}
/* 指示器的相关样式 */
.indicator {
position: absolute;
width: 100%;
height: 50px;
bottom: 0px;
text-align: center;
background-color: rgba(0, 0, 0, 0);
}
/* 未激活的指示器的样式 */
.indicator-item {
display: inline-block;
width: 20px;
height: 3px;
background: #fff;
cursor: pointer;
margin: 0 5px;
}
/* 激活指示器的样式 */
.indicator-item.select {
background: #ffa44f;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.carousel-container {
position: relative;
width: 500px;
height: 300px;
/* outline: 1px solid; */
margin: 0 auto;
margin-top: 20px;
overflow: hidden;
}
/* 图片的相关样式 */
.carousel-list {
width: 500%;
height: 100%;
}
.carousel-item {
width: 500px;
height: 100%;
object-fit: cover;
}
/* 左右箭头相关样式及背景样式 */
.arrow-left,
.arrow-right {
position: absolute;
background-color: rgba(0, 0, 0, 0.2);
width: 50px;
height: 100%;
top: 0;
color: #fff;
text-align: center;
line-height: 300px;
}
.arrow-left:hover,
.arrow-right:hover {
background-color: rgba(0, 0, 0, 0.5);
}
.arrow-left i {
cursor: pointer;
}
.arrow-right i {
cursor: pointer;
}
.arrow-left {
left: 0;
}
.arrow-right {
right: 0;
}
/* 指示器的相关样式 */
.indicator {
position: absolute;
width: 100%;
height: 50px;
bottom: 0px;
text-align: center;
background-color: rgba(0, 0, 0, 0);
}
/* 未激活的指示器的样式 */
.indicator-item {
display: inline-block;
width: 20px;
height: 3px;
background: #fff;
cursor: pointer;
margin: 0 5px;
}
/* 激活指示器的样式 */
.indicator-item.select {
background: #ffa44f;
}
(function() {
// 创建图片数组
var img = [
'./image/Wallpaper1.jpg',
'./image/Wallpaper2.jpg',
'./image/Wallpaper3.jpg',
'./image/Wallpaper4.jpg',
'./image/Wallpaper5.jpg',
];
// 封装document.querySelector
function $(selector) {
return document.querySelector(selector);
}
// 获取需要元素的dom
var doms = {
container: $('.carousel-container'),
list: $('.carousel-list'),
arrowLeft: $('.icon-lunbotujiantou-'),
arrowRight: $('.icon-lunbotujiantou-1'),
indicator: $('.indicator')
};
// 初始化
// console.log(dom.list);
// 当前显示的是第几张图片
var curIndex = 0;
function init() {
// 创建图片
function createImg(srcImg) {
// 创建img元素
var createImgs = document.createElement('img');
// 给img元素的src属性赋值,
createImgs.src = srcImg;
// 给img元素的class选择器属性写值
createImgs.className = 'carousel-item';
// 将img元素添加进class选择器为carousel-list的里面
doms.list.appendChild(createImgs);
}
// 遍历img数组,
for (var i = 0; i < img.length; i++) {
// 调用createImg函数,img[i]表示的就是img数组中表示的所有的图片地址,并传入createImg函数。
createImg(img[i]);
}
// 将第一张图片在最后重新添加一次
createImg(img[0]);
// 设置容器的宽度,
doms.list.style.width = doms.list.children.length + '00%';
// 创建指示器
/* function newindicator(span) {
var span = document.createElement('span');
// console.log(span);
span.className = 'indicator-item';
doms.indicator.appendChild(span);
// for (var j = 0; j < img.length; j++) {
// }
// span.classNama = 'indicator-item';
// doms.indicator.appendChild(span);
} */
function indicarors(span) {
// 创建span标签
var span = document.createElement('span');
// 给span标签的class选择器写入值
span.className = 'indicator-item';
// 将span元素添加进class选择器为indicator的里面
doms.indicator.appendChild(span);
}
// 同上面的操作一样,表示img数组中有几张图片就会有几个span标签
for (var i = 0; i < img.length; i++) {
indicarors(img[i]);
}
// 将第一张图片在最后重新添加一次
// createImg(img[0]);
// 设置指示器的激活状态
activationIndicator();
}
// 调用init函数,将视口内容显示出来
init();
// 指示器的状态
/* var act = $('.indicator-item.select');
console.log(act); */
function activationIndicator() {
// 先清除已经激活的指示器
// 先获取有显示激活指示器的样式的dom
var act = $('.indicator-item.select');
// 判断是否有激活指示器的dom
if (act) {
// 如果有就把激活指示器的样式换成没有激活的状态
act.className = 'indicator-item';
}
//激活指示器的颜色
// 改变图片
var index = curIndex;
// 如果图片大于所有图片
if (index > img.length - 1) {
// 就把图片改变为0,第一张
index = 0;
}
// 改变当前图片的对应的指示器样式,激活它。
doms.indicator.children[index].className = 'indicator-item select';
}
// 获取到视口的宽度
var containerWidth = doms.container.clientWidth;
// console.log(containerWidth);
// 动画总时间
var totalMS = 1000;
// 表示动画的效果是否运行
var isPlaying = false;
function moveTo(newIndex, onend) {
// 如果动画正在运行
if (isPlaying) {
// 就返回,不做任何执行操作
return;
}
// 如果要没有动画,所以要把动画设为true,告诉下面动画要执行了。
isPlaying = true;
// newIndex为要切换的图片索引
// 计算图片容器的marginLeft
// 将图片的mariginLeft变化到新的marginLeft
// 更新curIndex的值
// 更改指示器的状态
createAnimation({
// 当前时刻的起始值,是class选择器为carousel-list的marginLeft元素,取整数,如果没有就取0。
from: parseFloat(doms.list.style.marginLeft) || 0,
// 最终的:要切换的图片索引去乘以获取到的视口宽度
to: -newIndex * containerWidth,
totalMs: totalMS,
// n表示的就是marginLeft值
// 开始动画
onmove: function(n) {
// console.log(n);
// 改变class选择器为carousel-list的marginLeft值
doms.list.style.marginLeft = n + 'px';
},
// 终止动画
onend: function() {
// 将动画变成false,就是没有运行动画
isPlaying = false;
// 动画完成后调用这个onend函数,不传入参数,就不调用了
onend && onend();
},
})
// 更新图片的索引
curIndex = newIndex;
// 调用激活指示器的状态函数,用来设置指示器的状态(显示第几张,它对应的指示器就是激活状态)
activationIndicator();
}
function next() {
// 无缝切换到下一张图片
// 调用moveTo函数进行切换
// 最后一张图片切换完成后回归到第一张
// 切换到最后一张图片了
// 等动画完成后,要回到第一张图片
// 新的图片下标变为原来的图片下标+1
var newIndex = curIndex + 1;
// 变量的提升,定义一下onend。现在为undefined
var onend;
// 新的图片是否等于class选择器值为carousel-list的元素的子元素的长度-1,就是最后一张图片
if (newIndex === doms.list.children.length - 1) {
// 给onend赋值
onend = function() {
// 将marginLeft设为0,就是回到了第一张图片
doms.list.style.marginLeft = 0;
// 改变当前的图片为0.
curIndex = 0;
};
};
// 调用moveTo函数。执行相关的操作
moveTo(newIndex, onend);
}
function prev() {
// 无缝切换到上一张图片
// 调用moveTo函数进行切换
// 从第一张图片开始进行切换时,先跳到最后一张的位置,在进行切换
// 定义新的图片下标
var newIndex = curIndex - 1;
// 如果图片下标小于0
if (newIndex < 0) {
// 就定义最大的图片下标
var maxIndex = doms.list.children.length - 1;
// 设置marginLeft的大小为负的最大的图片下标乘以视口的宽度
doms.list.style.marginLeft = -maxIndex * containerWidth + 'px';
// 然后新的图片下标是最大下标-1
newIndex = maxIndex - 1;
}
// 调用moveTo函数
moveTo(newIndex);
}
// 处理指示器的点击事件
for (var i = 0; i < doms.indicator.children.length; i++) {
// 立即执行函数,
(function(i) {
doms.indicator.children[i].onclick = function() {
moveTo(i);
};
})(i);
}
// 点击左箭头向前一张
doms.arrowLeft.onclick = prev;
// 点击右箭头向右一张
doms.arrowRight.onclick = next;
// 定义计时器Id
var timeId;
// 间隔时间
var durations = 2000;
// 开始
function start() {
// 如果有计时器 已经有自动切换在进行
if (timeId) {
// 返回,啥都不干
return;
}
// 设置向下一张的计时器
timeId = setInterval(next, durations);
}
// 最开始自动切换
start();
// 停止
function stop() {
// 清除计时器
clearInterval(timeId);
// 计时器赋值为空。
timeId = null;
}
// 鼠标事件
// 鼠标移动上去停止播放动画
doms.container.onmouseenter = stop;
// 鼠标移开开始播放动画
doms.container.onmouseleave = start;
})()
若有错误,欢迎大佬们来给我指正哟!