移动端事件——移动端滑屏切换的幻灯片(二)
经过昨天对移动端基础的了解,今天就来用原生js实现一下我们的幻灯片。
因为是用原生实现,所以本文篇幅较长,各位看官只需理解思路即可,代码部分可以粗略看看。
毕竟我们有better-scroll这样封装好的框架能更快速实现效果。b( ̄▽ ̄)d
首先根据我们昨天的滑屏操作,先将幻灯片的滑屏效果做出来。这里大家将照片地址更换成自己的就能得到效果。
案例要在客户端才有效果哦,如果在pc端,网页中右键点审查,控制器旁边有个手机图标,点击下图这个也能有效果。
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="x-ua-compatible" content="ie=edge"> <title>document</title> <style> html { font-size: 10vw; } body { margin: 0; } ul { margin: 0; padding: 0; list-style: none; } #wrap { position: relative; width: 100vw; overflow: hidden; } #list { float: left; display: flex; display: -webkit-box; } #list li { flex: none; width: 100vw; } #list img { width: 100%; display: block; } .nav { position: absolute; left: 0; bottom: .2rem; width: 100%; text-align: center; vertical-align: top; } .nav a { display: inline-block; width: .3rem; height: .3rem; background: #fff; margin: 0 .1rem; border-radius: .15rem; transition: .3s; } .nav .active { width: .6rem; color: #fff; } </style> </head> <body> <div id="wrap"> <ul id="list"> <li><img src="img/banner01.png" /></li> <li><img src="img/banner02.png" /></li> <li><img src="img/banner03.png" /></li> <li><img src="img/banner04.png" /></li> </ul> <nav class="nav"> <a class="active"></a><a></a><a></a><a></a> </nav> </div> <script> // 幻灯片效果 { let wrap = document.queryselector("#wrap"); let list = document.queryselector("#list"); let navs = document.queryselectorall(".nav a"); let translatex = 0; //元素的移动位置 let startpoint = {}; //摁下时手指坐标 let dispoint = {};//手指移动距离 let startx = 0;//摁下时元素坐标 wrap.addeventlistener("touchstart",({changedtouches})=>{ startpoint = { x: changedtouches[0].pagex, y: changedtouches[0].pagey }; startx = translatex; }); wrap.addeventlistener("touchmove",(e)=>{ let touch = e.changedtouches[0]; let nowpoint = { x: touch.pagex, y: touch.pagey }; dispoint = { x: nowpoint.x - startpoint.x, y: nowpoint.y - startpoint.y }; translatex = dispoint.x + startx; list.style.transform = `translatex(${translatex}px)`; }); } </script> </body> </html>
滑屏效果做出来以后,我们给它添加动画,接下来为了大家方便看,就只写javascript的代码。红色的为添加的新代码。
<script> // 幻灯片效果 { let wrap = document.queryselector("#wrap"); let list = document.queryselector("#list"); let navs = document.queryselectorall(".nav a"); let translatex = 0; //元素的移动位置 let startpoint = {}; //摁下时手指坐标 let startx = 0;//摁下时元素坐标 let dispoint = {};//手指移动距离 let now = 0; // 记录当前在第几张 const range = .3* wrap.clientwidth; // 移动超过屏幕 30% 时,抬起切换到下一张 wrap.addeventlistener("touchstart",({changedtouches})=>{ list.style.transition = "none"; startpoint = { x: changedtouches[0].pagex, y: changedtouches[0].pagey }; startx = translatex; }); wrap.addeventlistener("touchmove",(e)=>{ let touch = e.changedtouches[0]; let nowpoint = { x: touch.pagex, y: touch.pagey }; dispoint = { x: nowpoint.x - startpoint.x, y: nowpoint.y - startpoint.y }; translatex = dispoint.x + startx; list.style.transform = `translatex(${translatex}px)`; }); wrap.addeventlistener("touchend",()=>{ if(math.abs(dispoint.x)>range){ //切换到下一张 //console.log(dispoint.x/math.abs(dispoint.x)); now -= dispoint.x/math.abs(dispoint.x); // 求当前要看第几张 } translatex = -now*wrap.clientwidth; list.style.transition = ".3s"; list.style.transform = `translatex(${translatex}px)`; //console.log(now); }); } </script>
这一步我们将每次手指离开屏幕后进行判断,判断手指移动的距离是否超过30%,抬起则切换到下一张。我们在每次切换时添加了0.3s的延迟动画。因为这延迟动画在手指触屏时就会生效,所以我们在touchstart的时候将它清除。
我们实现上面效果后,可以发现会有划出去的风险,接下来我们要实现无缝滚动
在此之前,我们要了解实现无缝滚动的原理,下面我做了个图,一目了然,如果有不明白可以评论联系我。
原理就是:当用户往第一组的第一张滑动时,我们将他移动到第二组的第一张。
当用户网第二组最后一张滑动时,我们将他移动到第一组的最后一张。
<script> // 幻灯片效果 { let wrap = document.queryselector("#wrap"); let list = document.queryselector("#list"); let navs = document.queryselectorall(".nav a"); let translatex = 0; //元素的移动位置 let startpoint = {}; //摁下时手指坐标 let startx = 0;//摁下时元素坐标 let dispoint = {};//手指移动距离 let now = 0; // 记录当前在第几张 const range = .3* wrap.clientwidth; // 移动超过屏幕 30% 时,抬起切换到下一张 list.innerhtml += list.innerhtml; // 把 list 图片复制一份 wrap.addeventlistener("touchstart",({changedtouches})=>{ list.style.transition = "none"; startpoint = { x: changedtouches[0].pagex, y: changedtouches[0].pagey }; if(now === 0){ now = navs.length; } else if(now === navs.length*2-1){ now = navs.length - 1; } translatex = -now*wrap.clientwidth; list.style.transform = `translatex(${translatex}px)`; startx = translatex; }); wrap.addeventlistener("touchmove",(e)=>{ let touch = e.changedtouches[0]; let nowpoint = { x: touch.pagex, y: touch.pagey }; dispoint = { x: nowpoint.x - startpoint.x, y: nowpoint.y - startpoint.y }; translatex = dispoint.x + startx; list.style.transform = `translatex(${translatex}px)`; }); wrap.addeventlistener("touchend",()=>{ if(math.abs(dispoint.x)>range){ //切换到下一张 //console.log(dispoint.x/math.abs(dispoint.x)); now -= dispoint.x/math.abs(dispoint.x); // 求当前要看第几张 } translatex = -now*wrap.clientwidth; list.style.transition = ".3s"; list.style.transform = `translatex(${translatex}px)`; }); }
实现这一效果,首先我们将图片复制多一份,然后在手指点击屏幕时,
判断现在是否是第一组的第一张,是则跳转到第第二组的第一张
或者是否为第二组的最后一张,是则跳转到第一组的最后一张。
对其style里的translatex进行进行更改就能实现无缝滚动。
接着我们对下面的白点进行同步,只用在touchend下添加以下代码即可
// 同步 nav navs.foreach(item=>{ item.classlist.remove("active"); }); navs[now%navs.length].classlist.add("active");
至此我们已经完成一个比较简单的移动端轮播。
但是
我们还有bug,就是当用户斜向上滑动时,轮播与下面内容皆会滑动。
因此我们需要做:
1. 判断用户想要滑动的是幻灯片,还是想要滚动滚动条
2. 如果想要滑动幻灯片,就阻止滚动条
3. 如果想要滚动滚动条,就阻止幻灯片
4. 注意 一旦在滑动的过程中判断到用户的滑动方向之后,就不在做方向修改
以下便是所有效果
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="x-ua-compatible" content="ie=edge"> <title>document</title> <style> html { font-size: 10vw; } body { margin: 0; } ul { margin: 0; padding: 0; list-style: none; } #wrap { position: relative; width: 100vw; overflow: hidden; } #list { float: left; display: flex; display: -webkit-box; } #list li { flex: none; width: 100vw; } #list img { width: 100%; display: block; } .nav { position: absolute; left: 0; bottom: .2rem; width: 100%; text-align: center; vertical-align: top; } .nav a { display: inline-block; width: .3rem; height: .3rem; background: #fff; margin: 0 .1rem; border-radius: .15rem; transition: .3s; } .nav .active { width: .6rem; color: #fff; } .textlist { margin: 0; padding: 0; list-style: none; } .textlist li { font: 14px/40px "宋体"; padding-left: 20px; border-bottom: 1px solid #000; } </style> </head> <body> <div id="wrap"> <ul id="list"> <li><img src="img/banner01.png" /></li> <li><img src="img/banner02.png" /></li> <li><img src="img/banner03.png" /></li> <li><img src="img/banner04.png" /></li> </ul> <nav class="nav"> <a class="active"></a><a></a><a></a><a></a> </nav> </div> <ul class="textlist"> </ul> <script> "use strict"; // 补充列表内容 { var txtlist = document.queryselector(".textlist"); txtlist.innerhtml = [...(".".repeat(100))].map(function (item, index) { return "<li>\u8fd9\u662f\u7b2c" + index + "\u4e2ali</li>"; }).join(""); } // 幻灯片 /* 判断用户滑动方向时: 一旦判断到用户的滑动方向,就认定该次操作用户想要进行上下或左右滑动,中间不再修改,一直到用户下一次再执行 start */ { var wrap = document.queryselector("#wrap"); var list = document.queryselector("#list"); list.innerhtml += list.innerhtml; // 把图片复制一份用来处理无缝 var startpoint = {}; // 摁下时手指位置 var startx = 0; // 摁下时元素的位置 var translatex = 0; // 元素的 tranlatex 值 var now = 0; //记录当前在第几张 var navs = document.queryselectorall(".nav a"); var range = wrap.clientwidth * .3; //超过该幅度切换上一张下一张 var ismove = false; // 是否需要滑动幻灯片 var isdir = true; // 记录是否已经判断到了方向 true 还没有判断到方向,false已经判断到了方向 wrap.addeventlistener("touchstart", function (_ref) { var changedtouches = _ref.changedtouches; list.style.transition = "none"; var touch = changedtouches[0]; startpoint = { x: touch.pagex, y: touch.pagey }; if (now == 0) { //第1组第0张会有划出去的风险 now = navs.length; } else if (now == navs.length * 2 - 1) { // 第2组最后一张,有划出去的风险 now = navs.length - 1; } translatex = -now * wrap.clientwidth; list.style.webkittransform = list.style.transform = "translatex(" + translatex + "px)"; startx = translatex; ismove = false; isdir = true; }); wrap.addeventlistener("touchmove", function (e) { var touch = e.changedtouches[0]; var nowpoint = { x: touch.pagex, y: touch.pagey }; var dis = { x: nowpoint.x - startpoint.x, y: nowpoint.y - startpoint.y }; // 判断方向根据需求来阻止默认事件 if (isdir) { if (math.abs(dis.x) - math.abs(dis.y) > 5) { // 左右滑动 ismove = true; isdir = false; } else if (math.abs(dis.y) - math.abs(dis.x) > 5) { // 上下滑动 ismove = false; isdir = false; } e.preventdefault(); } console.log(ismove, isdir); if (ismove) { translatex = startx + dis.x; list.style.webkittransform = list.style.transform = "translatex(" + translatex + "px)"; e.preventdefault(); } }); wrap.addeventlistener("touchend", function (_ref2) { var changedtouches = _ref2.changedtouches; var touch = changedtouches[0]; var nowpoint = { x: touch.pagex, y: touch.pagey }; var dis = { x: nowpoint.x - startpoint.x, y: nowpoint.y - startpoint.y }; // 当移动的距离超过图片宽度的 30% 时 切换至下一张或上一张,否则回到当前张 if (math.abs(dis.x) >= range && ismove) { // 切换上一张下一张 //console.log(dis.x,dis.x/math.abs(dis.x)); now -= dis.x / math.abs(dis.x); } //console.log(-now*wrap.clientwidth); translatex = -now * wrap.clientwidth; list.style.transition = ".3s"; list.style.webkittransform = list.style.transform = "translatex(" + translatex + "px)"; navs.foreach(function (nav) { nav.classlist.remove("active"); }); navs[now % navs.length].classlist.add("active"); }); } </script> </body> </html>