原生js实现响应式轮播图,支持电脑端点击切图,手机端滑动切图
轮播图的实现原理并不难,但是步骤有些繁琐。最近练习了一个轮播图,大部分是跟着网上的教程写的,然后自己做了一点兼容ie8的修改,加了点击切换图片的特效和手机端的滑动特效,让这个轮播图可以在响应式的网站中使用,同时兼容pc端和触屏端。
轮播图的样式也分很多种,淡入淡出的轮播图很容易实现,只需要把图片全都叠在一起,让相应的图片轮流显示就行了,但是滚动能的轮播图就要复杂很多。这里介绍的是滚动的轮播图:
原理:
实现的原理就是将所有的图片横向的排列起来,排成一个长方形,让这个长方形的总体不断平移,从而使图片轮流显示在div中,然后将div之外的隐藏起来就可以了
下面是详细步骤:
第一步,基本骨架
1)将要轮播的所有的图片都放到一个无序列表中
<div id="outer">
<ul id="imgList">
<li><img src="img/1.jpg"/></li>
<li><img src="img/2.jpg"/></li>
<li><img src="img/3.jpg"/></li>
<li><img src="img/4.jpg"/></li>
<li><img src="img/5.jpg"/></li>
</ul>
</div>
2)所有的li标签左浮动,只要ul的宽度够大,所有的图片就会横向的排列起来。
#imgList>li{
float: left;
}
3)让图片的宽高等于div的宽高,这样做不但能够提高开发的效率,还更便于网站的维护,即使图片源的尺寸都是不一样的,也能正常显示:
//getCompteredStyle方法和current方法作用相同,都是获取dom对象的属性,前者不兼容ie8,后者不兼容firefox,这样写可以解决兼容性的问题。
function getStyle(obj, name) {
if(window.getComputedStyle) {
return getComputedStyle(obj, null)[name];
} else {
return obj.currentStyle[name];
}
}
//-- 获取outer的宽度
var getOuterWidth = getStyle(outer,"width");
//去掉获取到的宽度值的px,使成为number类型的变量,方便计算
var widthObject = getOuterWidth.match(/\d*/);
var width = widthObject[0];
第二步,添加几个导航点和两个按钮
<div id="navContainer">
<!--我们就是导航点 -->
<a href="javascript:;"></a>
<a href="javascript:;"></a>
<a href="javascript:;"></a>
<a href="javascript:;"></a>
<a href="javascript:;"></a>
</div>
<!--我们是左右切换按钮-->
<a href="javascript:;" id="picLbtn"><img src="img/pic/picLbtn.png"></a>
<a href="javascript:;" id="picRbtn"><img src="img/pic/picRbtn.png"></a>
1)这里的圆点使用a标签来做,a标签为内联元素,是不能调整大小的。这里使用float:left给他设置为右浮动之后他就会转化为块级元素。当然,直接使用display:block也是可以的:
#navContainer>a{
z-index: 5;
float: left;
width: 15px;
height: 15px;
background-color: red;
margin: 0 5px;
opacity: 0.5;
filter: alpha(opacity=50);
border-radius: 100%;
}
这里把a标签的数量写固定了,如果上面放置了十张图片轮播,下面就要写十个导航点。
可以根据图片的数量动态的设置a标签的数量,不过这样写意义不大:
获取所有的imgList中所有的li标签放在对象x中,
for(i=0,i<x.length-1){
x.appendchild(a)//每次循环向数组中添加一个
}
a.href=javascript:;
2)添加两张图片作为向左向右切换的按钮,这种图片很多网站都有,如果不会ps的话随便找个带轮播图的网站占下来就可以用
3)导航点和轮播图的位置要继承div的位置,并且要动态的设置:
//(大div的宽度 - 导航区域的宽度)/ 2 = 导航区域的左边距
navContainer.style.left = (outer.offsetWidth - navContainer.offsetWidth)/2 + "px";
//(大div的高度 - 按钮区域的高度)/ 2 = 按钮区域的上边距
var tempBtnTop = (outer.offsetHeight - picLbtn.offsetHeight)/2 + "px"; picLbtn.style.top = tempBtnTop; picRbtn.style.top = tempBtnTop;
这样,这样不管大div改成多大,都不需要修改图片和导航点的定位,这个思想后面会多次体现。
第三步,自动切换图片
1)这里需要写一个move函数,作用是设置ul移动的平移效果。调用该函数时需要传递四个参数:要执行动画的对象,执行对象的属性,执行对象的目标,速度,回调函数
function move(obj, attr, target, speed, callback){
//关闭上一个定时器
clearInterval(obj.timer);
//获取元素目前的位置
var current = parseInt(getStyle(obj, attr));
//判断速度的正负值
//如果从0 向 800移动,则speed为正
//如果从800向0移动,则speed为负
if(current > target) {
//此时速度应为负值
speed = -speed;
}
//开启一个定时器,用来执行动画效果
//向执行动画的对象中添加一个timer属性,用来保存它自己的定时器的标识
obj.timer = setInterval(function(){
//获取box1的原来的left值
var oldValue = parseInt(getStyle(obj, attr));
//在旧值的基础上增加
var newValue = oldValue + speed;
//判断newValue是否大于800
//从800 向 0移动
//向左移动时,需要判断newValue是否小于target
//向右移动时,需要判断newValue是否大于target
if((speed < 0 && newValue < target) || (speed > 0 && newValue > target)) {
newValue = target;
}
//将新值设置给box1
obj.style[attr] = newValue + "px";
//当元素移动到0px时,使其停止执行动画
if(newValue == target) {
//达到目标,关闭定时器
clearInterval(obj.timer);
//动画执行完毕,调用回调函数
callback && callback();
}
}, 30);
}
2)自动轮播效果主要由autoChange函数实现,index变量再0到(imgArr.length - 1)之间不断循环,就实现了图片从一直循环展示
将偏移量设置为(-width*index),index函数每次都是累加的,这样每次的偏移量都是width的值,也就是图片的宽度
这段代码方放在setInterval函数中,setInterval函数的时间参数就是轮播图的轮播速度
function autoChange(){
//--这个定时器可以控制图片停留的时间
//--每隔三秒执行一次,不断的被执行图片就不断切换
timer = setInterval(function(){
index++;
//--index对长度取余,就可以让index一直在范围内循环
index %= imgArr.length;
//--这个move实现自动切换的平移效果
move(imgList , "left" , -width*index , 20 , function(){
//--执行move函数时调用set方法,将图片颜色改变,并且首末过渡
setA();
setRed();
});
},3000);
}
3)从最后一张图片到第一张需要一个过渡效果,就是前面原理图中的首尾相连
首先要在最后一张的图片中再添加第一张图片
<ul id="imgList">
<li><img src="img/pic/1.jpg"/></li>
<li><img src="img/pic/2.jpg"/></li>
<li><img src="img/pic/3.jpg"/></li>
<li><img src="img/pic/4.jpg"/></li>
<li><img src="img/pic/5.jpg"/></li>
<li><img src="img/pic/1.jpg"/></li>
</ul>
然后加一个判断,当执行的最后一张时,让他index立即变成0,当前显示的图片也会由最后一张变成第一张,而且因为最后一张和第一张时完全相同的,所以这个改变也是看不出来的,但是因为索引改变了,其他的也就跟着变成第一张了,再次自动循环的时候,就会自动变成第二张了。
function setA(){
//判断当前索引是否是最后一张图片
if(index >= imgArr.length - 1){
//--如果当前图片是最后一张,将该图片立即变为第一张
index = 0;
imgList.style.left = 0;
}
}
4)改变下面导航点的颜色,循环到哪个图片,对应的导航点就会变成黑色,其他的则为红色:
function setRed(){
for(var i=0 ; i<allA.length ; i++){
//--去掉内联样式里的颜色,显示样式表里的红色
//--如果这里不这么写的话,他就会每次遍历到某个图片,就会把他的导航点变成黑色,不会变回来
//--这么写每次调用都会先把大家都变成红色,再把需要的变成黑色
allA[i].style.backgroundColor = "";
}
allA[index].style.backgroundColor = "black";
}
第四步,点击导航点切换图片
1)写一个for循环,0到长度减1赋值给allA对象的num属性,这样每个导航点都对应着一个下标:
2)当点击导航点时,触发事件,将index设置为当前的num值,在执行move函数,他的偏移量就是改变后的-width * index,图片就会跳转到当前导航点对应的图片,具体代码如下:
var allA = document.querySelectorAll("#navContainer>a");
for(var i=0; i<allA.length ; i++){
//为每一个超链接都添加一个num属性
allA[i].num = i;
allA[i].onclick = function(){
//--在点击时,要关闭自动切换的定时器
clearInterval(timer);
//--获取点击超链接的索引,并将其设置为index
index = this.num;
//--setA只控制导航点时的效果
setRed();
//这个move是实现点击切换的平移效果,每次调用只会执行一次
//--四个参数:要执行动画的对象,执行对象的属性,执行对象的目标,速度,回调函数
move(imgList , "left" , -width*index , 20 , function(){
//开启被上面clearInterval关闭的自动切换程序
autoChange();
});
}
在执行单击触发函数时要用clearInterval把自动切换图片的定时器timer关掉,不然单击切换图片和自动切换图片同时执行网页会非常混乱。
同时也要调用setA函数,要将当前点击的图片设置为导航点变为黑色。
第五步,点击左右按钮切换图片
1)关闭timer定时器
2)左边的点击按钮是显示当前图片左边的图片,右边的点击按钮是显示当前图片右边的图片。因此要进行如下判断:
如果当前是第一张:左边的点击按钮不工作
如果是当前是最后一张:右边的点击按钮不工作(为了方便理解,和代码中的是反着写的)
3)完成判断后,调用setA函数设置导航点的颜色,然后开启正常的循环:
picLbtn.onclick = function(){
clearInterval(timer);
if (index != 0){
index = index-1;
}
setRed();
move(imgList , "left" , -width*index , 20 , function(){
autoChange();
});
}
picRbtn.onclick = function(){
clearInterval(timer);
if (index != allA.length - 1) {
index = index+1;
}
setRed();
move(imgList , "left" , -width*index , 20 , function(){
autoChange();
});
}
}
第六步,滑动屏幕切换图片
这个滑动效果只有在手机端电脑端或者打开浏览器的toggle device toobar模式才能用,光用鼠标拖拽是没有用的!!
1)定义4个变量,用来存放一些数值:
var startX = 0; //记录起始 刚刚触摸的点的位置 x的坐标
var moveX = 0; //滑动的时候x的位置
var distanceX = 0; //滑动的距离
var isMove = false;//是否滑动了
2)定义手指触摸屏幕触发函数:
在手指触摸到屏幕时,将触摸点的横坐标保存到startX变量中:
outer.addEventListener('touchstart', function(e){
clearInterval(timer);
startX = e.touches[0].clientX; //--触摸点的横坐标
});
3)定义手指滑动触发函数:
touchmove事件是手指触摸在屏幕上就一直触发的,因此distanceX函数最终保存的值将是手指离开屏幕时的值,用这个值减去startX就是滑动的距离:
这里传递给move函数的偏移量值是(-width*index + distanceX),因此ul偏移的宽度是distanceX,就实现了图片跟随手指滑动的效果:
outer.addEventListener('touchmove',function(e){e
moveX = e.touches[0].clientX;//--获取手指当前接触屏幕位置的横坐标
distanceX = moveX - startX; //--移动的距离=现在-初始
isMove = true;
move(imgList , "left" , -width*index+distanceX , 20 , function(){});
});
4)定义手指离开屏幕触发函数
用div的宽度除以3(记作x),比较用刚才获得的滑动的距离(记作y)。
如果y > x,认为滑动有效
如果往左滑就是切右边的图
如果往右滑就是切左边的图
如果y < x,认为滑动的距离太短了,滑动无效,吸附回去
当前如果是第一张,还往右滑,或者是最后一张还能往左滑,同样认为滑动无效,吸附回去
outer.addEventListener('touchend', function(){
//--滑动超过 1/3才可以切换图片,否则即为无效,则吸附回去
//math.abs取绝对值
if(isMove && Math.abs(distanceX) > width/3){
if(distanceX > 0 && index != 0){ //上一张
index = index - 1;
}
else if(distanceX < 0 && index != imgArr.length - 2){ //下一张
index = index + 1;
}
move(imgList , "left" , -width*index, 20 , function(){});
}
else if(isMove && Math.abs(distanceX) < width/3){
move(imgList , "left" , -width*index, 20 , function(){});
}
setRed();
autoChange();
});
最后整理之后的代码就是这样:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title> <style type="text/css">
*{
margin: 0;
padding: 0;
}
#outer{
width: 500px;
height: 333px;
margin: 50px auto;
position: relative;
overflow: hidden;
}
#imgList{
list-style: none;
position: absolute;
left: 0px;
}
#imgList>li{
float: left;
}
#imgList>li>img{
width: 500px;
height: 333px;
}
#navContainer{
position: absolute;
bottom: 15px;
} #navContainer>a{
z-index: 5;
float: left;
width: 15px;
height: 15px;
background-color: red;
margin: 0 5px;
opacity: 0.5;
filter: alpha(opacity=50);
border-radius: 100%;
}
#navContainer>a:hover{
background-color: black;
}
#picLbtn, #picRbtn{
position: absolute;
}
#picLbtn{
left: 8px;
}
#picRbtn{
right: 8px;
}
</style>
<script type="text/javascript">
window.onload = function(){
var imgList = document.getElementById("imgList");
var navContainer = document.getElementById("navContainer");
var outer = document.getElementById("outer");
var picLbtn = document.getElementById("picLbtn");
var picRbtn = document.getElementById("picRbtn");
var imgArr = document.querySelectorAll("#imgList>li>img");
/*设置按钮居中*/
navContainer.style.left = (outer.offsetWidth - navContainer.offsetWidth)/2 + "px";
var tempBtnTop = (outer.offsetHeight - picLbtn.offsetHeight)/2 + "px";
picLbtn.style.top = tempBtnTop;
picRbtn.style.top = tempBtnTop;
//-- 获取元素样式,最低兼容ie8
function getStyle(obj, name) {
if(window.getComputedStyle) {
return getComputedStyle(obj, null)[name];
} else {
return obj.currentStyle[name];
}
}
//-- 获取outer的宽度
var getOuterWidth = getStyle(outer,"width");
var widthObject = getOuterWidth.match(/\d*/);
var width = widthObject[0];
//-- 根据图片的数量设置ul的总宽度
imgList.style.width = width*imgArr.length+"px";
function move(obj, attr, target, speed, callback){
clearInterval(obj.timer);
var current = parseInt(getStyle(obj, attr));
if(current > target) {
speed = -speed;
}
obj.timer = setInterval(function(){
var oldValue = parseInt(getStyle(obj, attr));
var newValue = oldValue + speed;
if((speed < 0 && newValue < target) || (speed > 0 && newValue > target)) {
newValue = target;
}
obj.style[attr] = newValue + "px";
if(newValue == target) {
clearInterval(obj.timer);
callback && callback();
}
}, 30);
}
//设置默认选中的效果
var index = 0;
var allA = document.querySelectorAll("#navContainer>a");
allA[index].style.backgroundColor = "black";
//-- 正常开启自动切换函数
autoChange();
function setA(){
if(index >= imgArr.length - 1){
index = 0;
imgList.style.left = 0;
}
}
function setRed(){
for(var i=0 ; i<allA.length ; i++){
allA[i].style.backgroundColor = "";
}
allA[index].style.backgroundColor = "black";
}
var timer;
//--自动切换图片
function autoChange(){
timer = setInterval(function(){
index++;
index %= imgArr.length;
move(imgList , "left" , -width*index , 20 , function(){
setA();
setRed();
});
},3000);
}
//--实现点击导航点切换图片
//--调用setA、move、autochange函数
for(var i=0; i<allA.length ; i++){
allA[i].num = i;
allA[i].onclick = function(){
clearInterval(timer);
index = this.num;
setRed();
move(imgList , "left" , -width*index , 20 , function(){
autoChange();
});
}
picLbtn.onclick = function(){
clearInterval(timer);
if (index != 0){
index = index-1;
}
setRed();
move(imgList , "left" , -width*index , 20 , function(){
autoChange();
});
}
picRbtn.onclick = function(){
clearInterval(timer);
if (index != allA.length - 1) {
index = index+1;
}
setRed();
move(imgList , "left" , -width*index , 20 , function(){
autoChange();
});
}
}
var startX = 0;
var moveX = 0;
var distanceX = 0;
var isMove = false;
outer.addEventListener('touchstart', function(e){
clearInterval(timer); //--清除定时器,要记得事件结束之后再打开
startX = e.touches[0].clientX; //--触摸点的横坐标
});
outer.addEventListener('touchmove',function(e){
moveX = e.touches[0].clientX;//--获取当前手的横坐标
distanceX = moveX - startX; //--移动的距离=现在-初始
isMove = true;//证明滑动过
move(imgList , "left" , -width*index+distanceX , 20 , function(){});
});
outer.addEventListener('touchend', function(){
if(isMove && Math.abs(distanceX) > width/3){
if(distanceX > 0 && index != 0){
index = index - 1;
}
else if(distanceX < 0 && index != imgArr.length - 2){
index = index + 1;
}
move(imgList , "left" , -width*index, 20 , function(){});
}
else if(isMove && Math.abs(distanceX) < width/3){
move(imgList , "left" , -width*index, 20 , function(){});
}
setRed();
autoChange();
});
}
</script>
</head>
<body>
<div id="outer">
<ul id="imgList">
<li><img src="img/pic/1.jpg"/></li>
<li><img src="img/pic/2.jpg"/></li>
<li><img src="img/pic/3.jpg"/></li>
<li><img src="img/pic/4.jpg"/></li>
<li><img src="img/pic/5.jpg"/></li>
<li><img src="img/pic/1.jpg"/></li>
</ul>
<div id="navContainer">
<a href="javascript:;"></a>
<a href="javascript:;"></a>
<a href="javascript:;"></a>
<a href="javascript:;"></a>
<a href="javascript:;"></a>
</div>
<a href="javascript:;" id="picLbtn"><img src="img/pic/picLbtn.png"></a>
<a href="javascript:;" id="picRbtn"><img src="img/pic/picRbtn.png"></a>
</div>
</body>
</html>
缺点和解决(这个一定要看!!!):
缺点:切换到手机端的时候:点击导航点和左右切换按钮会出现问题
原因:这个是因为系统把点击导航点和按钮也当成了滑动事件,这样就要同时相应两组事件,所以会出问题(俺是这么理解的,对不对也不知道)
解决:解决就很简单了,反正手机端也不需要点击导航点和切换按钮(没有人会在手机上用那玩意)
所以如果是用在响应式布局的话,判断一下屏幕宽度,小于660px:左右按钮display:none,然后把导航点事件移除,大于660px就什么都不做
如果用在纯手机端,就直接把导航点的事件和左右按钮代码删掉就行了
这个程序写的还是有点模块化思想的,四五六步的内容随便删掉哪个其他互不影响。
结尾:
最后奉上B站视频教程的链接https://www.bilibili.com/video/av34087791,上文的大部分内容都来自这组视频~
原生js实现响应式轮播图,支持电脑端点击切图,手机端滑动切图的更多相关文章
- 第二百五十一节,Bootstrap项目实战--响应式轮播图
Bootstrap项目实战--响应式轮播图 学习要点: 1.响应式轮播图 本节课我们要在导航条的下方做一张轮播图,自动播放最新的重要动态. 一.响应式轮播图 响应式轮播图 第一步,设置轮播器区域car ...
- 原生JS面向对象思想封装轮播图组件
原生JS面向对象思想封装轮播图组件 在前端页面开发过程中,页面中的轮播图特效很常见,因此我就想封装一个自己的原生JS的轮播图组件.有了这个需求就开始着手准备了,代码当然是以简洁为目标,轮播图的各个功能 ...
- 移动web——bootstrap响应式轮播图
基本介绍 1.bootstrap有轮播图的模板,我们只需要改动下就行. 2.这里我们将介绍桌面版本和移动版本最后是综合版本 桌面版本 1.这里的图片设置是有窍门的,不再去添加img标签,而是作为a标签 ...
- Bootstrap 学习笔记 项目实战 响应式轮播图
左右两个箭头可以随浏览器缩放进行移动 保持在图片中间 Html代码: <!DOCTYPE html> <html lang="zh-cn"> <hea ...
- 原生js写一个无缝轮播图插件(支持vue)
轮播图插件(Broadcast.js) 前言:写这个插件的原因 前段时间准备用vue加上网易云的nodejs接口,模拟网易云音乐移动端.因为想自己写一遍所有的代码以及加固自己的flex布局,所以没有使 ...
- 原生JS实现移动端轮播图
功能描述: 自动无缝轮播图片,底部小圆点跟图片保持一致:手指左右移动轮播图,移动距离大于50px播放下一张(或上一张),小于50px则回弹 具体功能实现: 1.定时器 自动轮播图片 先声明一个inde ...
- 原生js的懒人轮播图
<style> body{ margin: 0; padding: 0px;}#carousel{ margin: auto; /* 居中 */ width: 600px; /* 设置宽度 ...
- 分享大麦UWP版本开发历程-01.响应式轮播顶部焦点图
话说有一天,临近下班无心工作,在网上看各种文章,阅读到了一篇名为<聊聊大麦网UWP版的首页顶部图片联动效果的实现方法>(传递:http://www.cnblogs.com/hippieZh ...
- JavaScript响应式轮播图插件–Flickity
简介 flickity是一款自适应手机触屏滑动插件,它的API参数很丰富,包括对齐方式.循环滚动.自动播放.是否支持拖动.是否开启分页.是否自适应窗口等. 在线演示及下载 演示地址 下载页面 使用方法 ...
随机推荐
- dingo/api 使用 知识
Dingo 能为Laravel提供一整套包括从路由,到认证的RESTful API开发工具 Laravel & Lumen RESTFul API 扩展包:Dingo API(一) —— 安装 ...
- git gc干了啥
前几天在写升级项目的时候发现./git/objects/pack/下的idx和pack文件是只读的,用java在windows下删除会抛异常,然后把只读属性改掉就好了. 于是就想弄清楚这两个文件的作用 ...
- FJWC2018
晚上水到8:40,感觉药丸. 把电脑带回寝室,大半夜敲键盘…… bzoj5254红绿灯 泰迪每天都要通过一条路从家到学校,这条路的起点是泰迪家,终点则是学校. 这条路中间还有n个路口,从第i-1个路口 ...
- Dalvik 虚拟机和 Sun JVM 在架构和执行方面有什么本质区别?
目前我理解的是: 两者共同点: 都是解释执行 byte code 都是每个 OS 进程运行一个 VM,并执行一个单独的程序 在较新版本中(Froyo / Sun JDK 1.5)都实现了相当程度的 J ...
- golang变量-数据类型一
package main import "fmt" var t1 = 100 var t2 = 200 var t3 = 300 var ( u1 = 100 u2 = 200 u ...
- 两种RBAC权限控制模型详解
序言 由于最近一直卡在权限控制这个坎上,原来设计的比较简单的权限控制思路已经无法满足比较复杂一些的场景,因此一直在探索一种在大部分场景下比较通用的权限模型. 首先,这里说明一下两种RBAC权限模型分别 ...
- ImmutableMap不可使用null的问题
示例 在项目中有发现类似下方的代码, Map tmpParams = ImmutableMap.of( "extraInfos", ext.get("extraInfos ...
- HTTP请求模型
HTTP请求模型 HTTP请求模型 一.连接至Web服务器一个客户端应用(如Web浏览器)打开到Web服务器的HTTP端口的一个套接字(缺省为80). 例如:http://www.myweb.com: ...
- 数组工具类 Day07
package com.sxt.arraytest2; /* * 数组的工具类 */ import java.util.Arrays; public class TestArrays { public ...
- struts.xml中的结果类型与视图
实际上在Struts2框架中,一个完整的结果视图配置文件应该是: ? 1 2 3 4 5 <action name="Action名称" class="Action ...