移動端事件——移動端滑屏切換的幻燈片(二)

通過昨天對移動端基礎的瞭解,今天就來用原生JS實現一下咱們的幻燈片。html

由於是用原生實現,因此本文篇幅較長,各位看官只需理解思路便可,代碼部分能夠粗略看看。web

畢竟咱們有better-scroll這樣封裝好的框架能更快速實現效果。b( ̄▽ ̄)d 框架

首先根據咱們昨天的滑屏操做,先將幻燈片的滑屏效果作出來。這裏你們將照片地址更換成本身的就能獲得效果。flex

案例要在客戶端纔有效果哦,若是在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的代碼。紅色的爲添加的新代碼。ui

<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的時候將它清除。spa

 

咱們實現上面效果後,能夠發現會有劃出去的風險,接下來咱們要實現無縫滾動code

在此以前,咱們要了解實現無縫滾動的原理,下面我作了個圖,一目瞭然,若是有不明白能夠評論聯繫我。orm

 

 

 原理就是:當用戶往第一組的第一張滑動時,咱們將他移動到第二組的第一張。htm

      當用戶網第二組最後一張滑動時,咱們將他移動到第一組的最後一張。

<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>
相關文章
相關標籤/搜索