欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

移动端事件——移动端滑屏切换的幻灯片(二)

程序员文章站 2022-03-26 14:36:42
经过昨天对移动端基础的了解,今天就来用原生JS实现一下我们的幻灯片。 因为是用原生实现,所以本文篇幅较长,各位看官只需理解思路即可,代码部分可以粗略看看。 毕竟我们有better-scroll这样封装好的框架能更快速实现效果。b( ̄▽ ̄)d 首先根据我们昨天的滑屏操作,先将幻灯片的滑屏效果做出来。这 ......

经过昨天对移动端基础的了解,今天就来用原生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>