轮播
轮播图在网页搭建中经常遇到, 与轮播有关的卡片滑块也常见, 所以有必要理解其中的原理
轮播图HTML中应包含几个部分?
- 视觉窗口,即可见区域
- 包含所有图片的容器
- 具体的每一张轮播图片
- 锚点列表, 点击可跳到具体某一页
- 左右切换按钮
让每一个轮播页大小刚好等于视图窗口大小
- 设置视图窗口宽度, 让超过的部分隐藏
- 设置包含所有图片的容器的宽度为 10000%, 目的是为了让容器足够大. 因为如果容器不够大的话容器中每一张图片就算设定了固定宽度也还是会受到挤压导致一定程度的变形.
这里设置的宽度10000%为相对与父div(视图窗口)的100倍, 即100% * 100; 所以这个轮播图最多可装下100个轮播页面 - 设置每一个轮播页面的宽度为父div (容器) 1%, 刚好等于爷爷div (视图窗口) 的大小, 即 100% = 100% * 100 * 1% . 这时视图窗口div中就刚好可见一个轮播页.
每一次滚动图片JS需要做什么?
- 改变索引, 添加class使对应锚点设为选中状态
- 计算这次滚动到终点时包含所有图片的容器需要相对与视图窗口左移的位置
- 清空定时器(防止开启多个重名定时器导致图片滚动速度收到影响)
- 开启定时器(刷新速度一般为10~20ms), 每一次将图片移动一定的距离, 直达到达这次移动重点时清空定时器
如何让轮播图自动滚动?
设置一个定时器(刷新速度没几秒一次), 每次调用一次轮播的移动函数
如何实现无缝轮播(最后一张转到第一张时的衔接)
- 在最后一张轮播图后复制一份第一张轮播图, 放到最后, 让第一页和最后一页完全相同
- 最后一张轮播图移动到终点时将包含所有图片的容器的左偏移量设为0, 即最后一张滚动到终点时瞬间将第一张轮播图放置于视图窗口中, 看起来就无缝了
让滚动速度为匀减速运动
因为匀速运动的轮播不是很好看, 所以可以通过JS控制移动速度
在定时器中每一次移动距离可以设置为
移动距离 = (终点 - 当前位置) / 15;
这个15是随便写的, 终点 -当前位置在除以一个大于一的数, 这个数每次计算时都会变小, 所以成功做到了变速运动.
HTML
id="container">id="inner" class="clear"> style="background:red;" src="#1#" alt=""> style="background:orange;" src="#2#" alt=""> style="background:green;" src="#3#" alt=""> style="background:cyan;" src="#4#" alt=""> style="background:yellow;" src="#5#" alt=""> style="background:purple;" src="#6#" alt=""> style="background:pink;" src="#7#" alt=""> style="background:blue;" src="#8#" alt=""> style="background:red;" src="#1#" alt="">class="paganation" id="paganation"> class="selected">1 2 3 4 5 6 7 8id="left" class="arrow"> <id="right" class="arrow"> >
CSS
* { margin: 0; padding: 0; border: 0; } #container { width: 510px; height: 286px; margin: 0 auto; position: relative; background: pink; overflow: hidden; } #inner { width: 10000%;/*设置的宽度10000%为相对与视图窗口的100倍*/ height: 100%; position: absolute; left: 0px; top: 0px; } #inner img { width: 1%; height: 100%; float: left; } .paganation { width: 100%; position: absolute; bottom: 10px; text-align: center; } .paganation span { padding: 5px 8px; background: #F2F2F2; color: red; border-radius: 50%; cursor: pointer } .paganation .selected { background: #888; color: white; } .arrow { position: absolute; top: 0; width: 30px; height: 286px; line-height: 286px; text-align: center; color: white; cursor: pointer; } #right { right: 0; } .arrow:hover { background: rgba(0, 0, 0, 0.5); }
JS
var container = document.getElementById("container"); var inner = document.getElementById("inner"); var spanList = document.getElementById("paganation").getElementsByTagName("span"); var left = document.getElementById("left"); var right = document.getElementById("right"); var carousel = new Carousel(container, inner, spanList, left, right, 15, true);
Carousel 轮播类
因为轮播的实现需要定义许多变量并使用几个函数, 所以在此将其封装成类, 使用方式为上面JS部分, 依次传入 视图窗口dom, 包含所有图片的容器dom, 锚点dom数组, 左移按钮dom, 右移按钮dom, 移动速度控制(值越小移动越快), 是否自动滚动, 自动滚动周期 . 必传参数为前3个.没有进行判断和抛错, 也没有写属性设置, 只是简单封装一下
function Carousel(container, inner, spanList, left, right, speedControl = 15, autoRoll = true, slideCycle =5000) { this.container = container; //组件容器, 包括所有 this.inner = inner; //装载图片的容器 this.spanList = spanList; //每页锚点 this.left = left; //左移button this.right = right; //右移button this.speedControl= speedControl; //速度控制,值越小轮播跳转速度越快 this.autoRoll = autoRoll; //是否自动滚动 this.slideCycle = slideCycle; //滚动周期 this.timer; //完成滚动的定时器 var me = this; //解决定时器的this问题,以及绑定事件时的在文档中执行的this this.distance = this.container.offsetWidth;//获取展示区的宽度,即每张图片的宽度 // console.log(this.spanList.length); //左切换事件 if(left && right){ this.left.onclick= function() { if(me.clickFlag){ clickFlag = false; me.backward(); } } //右切换事件 this.right.onclick= function() { if(me.clickFlag){ clickFlag = false; me.forward(); } } } //这里只能用let, 用var会导致点击中的index全变成spanList.length for(let i=0; i < this.spanList.length; i++) { this.spanList[i].onclick= function() { me.index = i; me.routate(); } } //控制每次滚动间隔 (当类调用时自动执行) if(this.autoRoll) { //如果需要自动滚动(默认需要) this.timeAutoGo = setInterval(function(){ me.index++; //当图片下标到最后一张把小标换0 if(me.index > me.spanList.length){ me.index=0; } me.routate(); },this.slideCycle);//主要用来设置自动滑动的计时器 } } Carousel.prototype = { constructor: Carousel, index: 0, //记录每次滑动图片的下标 clickFlag: true, //设置左右切换标记位防止连续按 //定义图片滑动的函数 routate: function () { var start=this.inner.offsetLeft;//获取移动块移动前的left的开始坐标, (0,width, width*2, width*3 ...) var end=this.index*this.distance*(-1);//获取移动块移动结束的坐标。 //计算公式即当移动到第三张图片时,图片下标为2乘以图片的宽度就是块的left值。 var change=end-start;//偏移量 this.clear();//先把按钮状态清除,再让对应按钮改变状态 if(this.index==this.spanList.length){ this.spanList[0].className="selected"; }else{ this.spanList[this.index].className="selected"; } var me = this; clearInterval(me.timer);//开启计时器前先把之前的清除, 防止多次重复生成定时器,干扰操作, me.timer=setInterval(function() { var iCur = parseInt(me.getStyle(me.inner, 'left')); if(iCur == end){//当图片到达终点停止计时器 clearInterval(me.timer); me.clickFlag = false;//当图片到达终点才能切换 if(me.index == me.spanList.length ){ me.inner.style.left = 0; //无缝滚动 me.index=0; //当图片到最后一张时把它瞬间切换回第一张,由于都同一张图片不会影响效果 iCur = 0; end = 0; //让speed等于0, 即me.inner.style.left = 0;停止此次运动,否则结束时候会向左偏移 } } var speed = (end - iCur) / me.speedControl; speed = speed > 0 ? Math.ceil(speed): Math.floor(speed); me.inner.style.left = (speed + iCur) + "px"; //每次移动距离=change/maxT*ts }, 17);//每个17毫秒让块移动 }, //编写图片向右滑动的函数 forward : function() { this.index++; //当图片下标到最后一张把小标换0 if(this.index > this.spanList.length){ this.index=0; } this.routate(); }, //编写图片向左滑动函数 backward : function(){ this.index--; //当图片下标到第一张让它返回到倒数第二张, //left值要变到最后一张才不影响过渡效果 if(this.index<0){ this.index=this.spanList.length-1; this.inner.style.left=(this.index+1)*this.distance*(-1)+"px"; } this.routate(); }, //清除页面所有按钮状态颜色 clear : function() { for(var i=0;i<this.spanList.length;i++){ this.spanList[i].className=""; } }, getStyle : function(obj, attr) { if(obj.currentStyle != undefined ){ return obj.currentStyle[attr]; } else{ return getComputedStyle(obj, false)[attr]; } } }
网上的一些博客
我的入门教程
(这篇文章是我原创,之前发表在实验室的官网中,链接)