原生 JS 實(shí)現(xiàn)移動(dòng)端 Touch 滑動(dòng)反彈

什么是 Touch滑動(dòng)?就是類似于 PC端的滾動(dòng)事件,但是在移動(dòng)端是沒(méi)有滾動(dòng)事件的,所以就要用到 Touch事件結(jié)合 js去實(shí)現(xiàn),效果如下:

1. 準(zhǔn)備工作

什么是移動(dòng)端的 Touch事件?在移動(dòng)端 Touch事件可以細(xì)分成三種,分別是: touchstart、 touchmove和 touchend,并且 touch事件必須要用 addEventListener去監(jiān)聽(tīng)。愛(ài)掏網(wǎng) - it200.com

touchStart 當(dāng)手指觸碰到屏幕的時(shí)候觸發(fā)

touchmove 當(dāng)手指在屏幕上不斷移動(dòng)的時(shí)候觸發(fā)

touchend 當(dāng)手指離開屏幕的時(shí)候觸發(fā)

Touch 事件觸發(fā)的 Event 對(duì)象

// 手指觸碰到屏幕時(shí)觸發(fā)
element.addEventListener('touchstart',function(e){
// 打印的事件對(duì)象
console.log(e);
})
changedTouches、 targetTouches、 touches都是偽數(shù)組,里面裝的是手指列表。愛(ài)掏網(wǎng) - it200.com

三種返回對(duì)象的區(qū)別

其實(shí)這三種返回的對(duì)象,都是表示用戶觸摸事件時(shí)的手指信息,之所以是一個(gè)偽數(shù)組,是因?yàn)橛锌赡艹霈F(xiàn)多指同時(shí)觸摸,但是在實(shí)際工作中一般不去考慮多指的情況。愛(ài)掏網(wǎng) - it200.com而它們唯一的區(qū)別就是在 touchstart和 touchmove事件的時(shí)候, changedTouches、 targetTouches、 touches都能獲取到手指的信息,但是在 touchend事件的時(shí)候, targetTouches、 touches對(duì)象是不能返回離開屏幕時(shí)的手指信息,只有 changedTouches對(duì)象能返回。愛(ài)掏網(wǎng) - it200.com

有哪些手指信息?

我們可以看下上面的圖片,在 changedTouche[0]中,有些值:
clientX:74 // 觸摸點(diǎn)相對(duì)于瀏覽器的 viewport 左邊緣的 x 坐標(biāo),不會(huì)包括左邊的滾動(dòng)距離。愛(ài)掏網(wǎng) - it200.com
clientY:73 // 觸摸點(diǎn)相對(duì)于瀏覽器的 viewport 上邊緣的 Y 坐標(biāo),不會(huì)包括上邊的滾動(dòng)距離。愛(ài)掏網(wǎng) - it200.com
screenX:2202 // 觸摸點(diǎn)相對(duì)于屏幕左邊緣的 x 坐標(biāo)。愛(ài)掏網(wǎng) - it200.com
screenY:327 // 觸摸點(diǎn)相對(duì)于屏幕上邊緣的 Y 坐標(biāo)。愛(ài)掏網(wǎng) - it200.com
pageX:65 // 觸摸點(diǎn)相對(duì)于 document 的左邊緣的 x 坐標(biāo),包括左邊的滾動(dòng)距離
pageY:18 // 觸摸點(diǎn)相對(duì)于 document 的上邊緣的 Y 坐標(biāo),包括左邊的滾動(dòng)距離

2. 基本結(jié)構(gòu)

此案例模擬的是移動(dòng)端的一種滑動(dòng)菜單效果。愛(ài)掏網(wǎng) - it200.com

HTML部分

   
        
  • 列表一
  • 列表二
  • 列表三
  • 列表四
  • 列表五
  • 列表六
  • 列表七
  • 列表八
  • 列表九
  • 列表十

css部分

  /* 樣式初始化 */
 *{
    margin: 0;
    padding: 0;
}
 html,
 body{
     width: 100%;
}
aside {
   height: 100%;
   width: 100%;
}
 /* 列表的父盒子,限制寬高 */
 /* 注意設(shè)置overflow: hidden;樣式后,超出這個(gè)盒子的ul將不會(huì)顯示 */
 .draw {
    width: 60px;
    height: 500px;
    border: 2px solid #ccc;
    overflow: hidden;
    position: fixed;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
 }
 /* li 設(shè)置了浮動(dòng), 所以 ul 要清除浮動(dòng) */
 ul:after {
   content: "";
   display: block;
   visibility: hidden;
   height: 0;
   clear: both;
 }
 ul{
    zoom: 1;
}
li{
  list-style: none;
  float: left;
  width: 60px;
  height: 60px;
  line-height: 60px;
  text-align: center;
}

在列表的父盒子上設(shè)定一個(gè) overflow:hidden屬性,使超出盒子部分的列表暫時(shí)隱藏掉,后面會(huì)通過(guò) js去實(shí)現(xiàn)滑動(dòng)。愛(ài)掏網(wǎng) - it200.com

效果圖

3. 首次滑動(dòng)

手指觸摸到列表向下滑動(dòng)的時(shí)候,列表應(yīng)該跟著向下滑動(dòng),當(dāng)手指離開屏幕的時(shí)候,列表應(yīng)該停在滑動(dòng)的位置。愛(ài)掏網(wǎng) - it200.com這里就會(huì)用到上面準(zhǔn)備階段的知識(shí)點(diǎn)了,不明白的可以參考上面的概念。愛(ài)掏網(wǎng) - it200.com

實(shí)現(xiàn)原理

touchstart的時(shí)候,獲取手指觸摸的落點(diǎn) A,通過(guò)這個(gè)點(diǎn)對(duì)象里面的 clientY屬性,獲取距離頂部可視區(qū)的距離;
touchmove的時(shí)候,獲取手指的點(diǎn) B,同樣的獲取移動(dòng)時(shí)距離頂部可視區(qū)的距離;
touchmove的時(shí)候,還要做另一件事情,就是獲取兩點(diǎn)的差值( B.clientY-A.clientY),將這個(gè)差值動(dòng)態(tài)賦值給 ul, ul只需要設(shè)置向 Y軸方向偏移這個(gè)距離,就能實(shí)現(xiàn)列表隨手指滑動(dòng)

先來(lái)張示意圖,怎么通過(guò) js 讓列表滑動(dòng)起來(lái)

示例代碼

  var draw = document.querySelector('#draw');
  var ul = draw.children[0];
  // touchstart 時(shí),記錄手指在 Y 軸上的落點(diǎn)距離可視頂部距離
  var startY = 0;
  ul.addEventListener('touchstart', function (e) {
      startY = e.changedTouches[0].clientY;
   })
  // touchmove 時(shí),記錄此時(shí)手指在 Y 軸上的落點(diǎn)距離可視頂部距離
  ul.addEventListener('touchmove', function (e) {
    // 獲取差值
    var dy = e.changedTouches[0].clientY - startY;
    // 設(shè)置 ul 在 Y 軸上的偏移
    ul.style.transform = 'translateY(' + dy + 'px)';
})

效果圖

4. 再次滑動(dòng)

上面的效果圖,細(xì)心的朋友可能已經(jīng)發(fā)現(xiàn)了問(wèn)題,在第一次的時(shí)候,得到了我們想要的效果,但是在第二次的時(shí)候,我們繼續(xù)向下移動(dòng)了一段距離,但是 ul并沒(méi)有接著第一次的位置繼續(xù)向下,而是瞬間跳了上去。愛(ài)掏網(wǎng) - it200.com

問(wèn)題分析

雖然第二次是繼續(xù)向下移動(dòng)了一段距離,但是觸摸結(jié)束后,最終是將此時(shí)的差值,重新賦值給了 ul的 Y軸偏移,所以視覺(jué)上“跳了上去”。愛(ài)掏網(wǎng) - it200.com

解決方法:

每一次滑動(dòng)結(jié)束之后,都應(yīng)該記錄下此次滑動(dòng)的距離,與之前的進(jìn)行累加,待下一次滑動(dòng)的時(shí)候, ul在 Y軸的偏移值應(yīng)該是之前的距離加上本次滑動(dòng)的距離。愛(ài)掏網(wǎng) - it200.com
新增 touchend事件,在該事件里同樣的可以獲取到本次滑動(dòng)的距離,將它與上一次的距離相加,賦值給一個(gè)全局變量;
在 touchmove事件里有點(diǎn)小改動(dòng),就是在給 ul設(shè)置偏移值的時(shí)候,除了本次滑動(dòng)的差值還要加上這個(gè)上一次的值;

示意圖:

示例代碼

       var draw = document.querySelector('#draw');
        var ul = draw.children[0];
        var startY = 0;
        // 剛觸碰到屏幕的時(shí)的手指信息
        var centerY = 0;
        // 用來(lái)記錄每次觸摸時(shí)上一次的偏移距離
        // touchstart 時(shí),記錄手指在 Y 軸上的落點(diǎn)距離可視頂部距離
        ul.addEventListener('touchstart', function (e) {
            startY = e.changedTouches[0].clientY;
        });

        // touchmove 時(shí),記錄此時(shí)手指在 Y 軸上的落點(diǎn)距離可視頂部距離
        ul.addEventListener('touchmove', function (e) {
        // 獲取差值
            var dy = e.changedTouches[0].clientY - startY;

       // 上次的滑動(dòng)距離加上本次的滑動(dòng)距離
            var tempY = centerY + dy;

        // 設(shè)置 ul 在 Y 軸上的偏移
            ul.style.transform = 'translateY(' + tempY + 'px)';
        });

        // touchend 時(shí),記錄此時(shí)手指在 Y 軸上的落點(diǎn)距離可視頂部距離
        ul.addEventListener('touchend', function (e) {
            // 獲取差值
            var dy = e.changedTouches[0].clientY - startY;

            // 記錄移動(dòng)的距離
            centerY = centerY + dy;
        });

效果圖

5. 限制滑動(dòng)區(qū)間

到上面一步,我們已經(jīng)可以實(shí)現(xiàn)列表的滑動(dòng)了,但是也存在一個(gè)問(wèn)題,就是向上或者向下的時(shí)候沒(méi)有限制,上下可以無(wú)限的滑動(dòng),甚至再用點(diǎn)力,就看不到列表了。愛(ài)掏網(wǎng) - it200.com為了美觀和實(shí)用,這樣肯定不行的,需要給它設(shè)定一個(gè)區(qū)間,設(shè)定向上或者向下最多只能留白多少。愛(ài)掏網(wǎng) - it200.com

限制向下滑動(dòng)最大區(qū)間

設(shè)定向下最大區(qū)間的值比較簡(jiǎn)單,直接設(shè)定一個(gè)值,當(dāng)上一次滑動(dòng)的距離加上本次滑動(dòng)的距離大于這個(gè)值的時(shí)候,就不讓它再繼續(xù)往下滑了,讓他直接等于這個(gè)設(shè)定的值。愛(ài)掏網(wǎng) - it200.com

示例代碼

 // 設(shè)定一個(gè)最大向下滑動(dòng)的距離
        // touchmove 時(shí),記錄此時(shí)手指在 Y 軸上的落點(diǎn)距離可視頂部距離
        ul.addEventListener('touchmove', function (e) {
           // 獲取差值
            var dy = e.changedTouches[0].clientY - startY;

           // 上次的滑動(dòng)距離加上本次的滑動(dòng)距離

            var tempY = centerY + dy;

          // 當(dāng)上次滑動(dòng)的距離加上本次滑動(dòng)的距離 大于 設(shè)定的最大向下距離的時(shí)候
            if (tempY > maxDown) {

           // 直接讓偏移的值 等于這個(gè)設(shè)定值
                tempY = maxDown;

                    }

            // 設(shè)置 ul 在 Y 軸上的偏移
            ul.style.transform = 'translateY(' + tempY + 'px)';
        })

限制向上滑動(dòng)最大區(qū)間

向上滑動(dòng)時(shí),當(dāng) ul的底部距盒子底部的距離大于設(shè)定值的時(shí)候,不讓其繼續(xù)向上滑動(dòng),關(guān)鍵是這個(gè)值怎么去判斷?

求出向上滑動(dòng)最大值
注意:因?yàn)?ul是向上滑動(dòng)的,所以求得的距離前面要加上一個(gè)負(fù)號(hào)( -)

示例代碼

  // 設(shè)定一個(gè)最大向下滑動(dòng)的距離
        var maxDown = 50;

        // 求得一個(gè)最大向上滑動(dòng)的距離
        var maxUp = -(ul.offsetHeight - draw.offsetHeight + maxDown);

        // touchmove 時(shí),記錄此時(shí)手指在 Y 軸上的落點(diǎn)距離可視頂部距離
        ul.addEventListener('touchmove', function (e) {
          // 獲取差值
            var dy = e.changedTouches[0].clientY - startY;

          // 上次的滑動(dòng)距離加上本次的滑動(dòng)距離
            var tempY = centerY + dy;

          // 當(dāng)上次滑動(dòng)的距離加上本次滑動(dòng)的距離 大于 設(shè)定的最大向下距離的時(shí)候

            if (tempY > maxDown) {

                tempY = maxDown;

               }// 當(dāng)上次滑動(dòng)的距離加上本次滑動(dòng)的距離 小于 設(shè)定的最大向上距離的時(shí)候
            else if (tempY 

效果圖

認(rèn)真觀察上圖,雖然成功的設(shè)置了最大滑動(dòng)區(qū)間,但是你有沒(méi)有發(fā)現(xiàn),一直往一個(gè)方向滑動(dòng)的時(shí)候,雖然列表不會(huì)繼續(xù)往下滑動(dòng),但是接著往相反方向滑動(dòng)的時(shí)候,感覺(jué)列表滑不動(dòng),需要滑一段距離后,列表才會(huì)跟著走,這是為什么呢?因?yàn)榛瑒?dòng)的過(guò)程 centerY是一直變的,列表雖然視覺(jué)上不動(dòng)了,但是在 touchend事件的時(shí)候,它的 centerY值一直在累加。愛(ài)掏網(wǎng) - it200.com解決方法請(qǐng)往下看。愛(ài)掏網(wǎng) - it200.com

6. 設(shè)定反彈區(qū)間

“滑動(dòng)反彈”,這里的反彈是本篇文章的最后一步,上面說(shuō)到的問(wèn)題,就在這里解決。愛(ài)掏網(wǎng) - it200.com因?yàn)槊恳淮斡|發(fā) touchend事件的時(shí)候, centerY值就累加一次,所以需要在 touchend事件里做判斷。愛(ài)掏網(wǎng) - it200.com我們?cè)O(shè)定一個(gè)反彈區(qū)間,就是當(dāng) centerY的值大于或者小于某個(gè)值的時(shí)候,讓它觸發(fā)反彈。愛(ài)掏網(wǎng) - it200.com

設(shè)定向上反彈值

向上的值比較簡(jiǎn)單,設(shè)置成“ 0”。愛(ài)掏網(wǎng) - it200.com為什么是“ 0”呢?我們限定只要手指離開時(shí),上一次的滑動(dòng)距離加上本次的距離 >0的時(shí)候,就讓它觸發(fā)反彈,并且反彈回 0點(diǎn)的位置,也就是兩次滑動(dòng)的距離和 =0。愛(ài)掏網(wǎng) - it200.com

示例代碼

     // 向上反彈
        var maxUpBounce = 0;
        // touchend 時(shí),記錄此時(shí)手指在 Y 軸上的落點(diǎn)距離可視頂部距離
        ul.addEventListener('touchend', function (e) {

            // 獲取差值
            var dy = e.changedTouches[0].clientY - startY;

            // 記錄移動(dòng)的距離
            centerY = centerY + dy;

           // 兩次滑動(dòng)的距離 大于 設(shè)定的 向上 反彈值時(shí)

            if (centerY > maxUpBounce) {
           // 讓兩次滑動(dòng)的距離 等于 設(shè)置的值
                centerY = maxUpBounce;
           // 添加過(guò)渡
                ul.style.transition = 'transform .5s';
                ul.style.transform = 'translateY(' + centerY + 'px)';

            }
        })

設(shè)定向下反彈值

向下的值其實(shí)跟之前求滑動(dòng)區(qū)間差不多,我們參考下圖,當(dāng)列表向上滑動(dòng),滑動(dòng)到列表底部的時(shí)候,只要此時(shí)再向上滑動(dòng),就讓它向下反彈。愛(ài)掏網(wǎng) - it200.com向下反彈值就是 -(ul.offsetHeight-draw.offsetHeight),只要滑動(dòng)的差值小于這個(gè)設(shè)定值,就讓它向下反彈,并且反彈回設(shè)定值的位置。愛(ài)掏網(wǎng) - it200.com

示例代碼

     // 向上反彈值
        var maxUpBounce = 0;

        // 向下反彈值
        var maxDownBounce = -(ul.offsetHeight - draw.offsetHeight);

        // touchend 時(shí),記錄此時(shí)手指在 Y 軸上的落點(diǎn)距離可視頂部距離
        ul.addEventListener('touchend', function (e) {

        // 獲取差值
            var dy = e.changedTouches[0].clientY - startY;

         // 記錄移動(dòng)的距離
            centerY = centerY + dy;

        // 兩次滑動(dòng)的距離 大于 設(shè)定的 向上 反彈值時(shí)
            if (centerY > maxUpBounce) {

        // 讓兩次滑動(dòng)的距離 等于 設(shè)置的值
               centerY = maxUpBounce;

          // 添加過(guò)渡
               ul.style.transition = 'transform .5s';
               ul.style.transform = 'translateY(' + centerY + 'px)';

            }// 兩次滑動(dòng)的距離 小于 設(shè)定的 向下 反彈值時(shí)
            else if (centerY 

注意:在 touchend事件的時(shí)候,給列表添加了 transition屬性才會(huì)有反彈的效果,但是,下一次滑動(dòng)的時(shí)候, touchmove事件的時(shí)候,這個(gè)屬性還存在,所以就會(huì)出現(xiàn)滑動(dòng)的時(shí)候有頓挫感,所以在 touchmove事件的時(shí)候,一進(jìn)來(lái)就清一下過(guò)渡 ul.style.transition='none';。愛(ài)掏網(wǎng) - it200.com

完成后效果圖

7. 完整代碼

    var draw = document.querySelector('#draw');
    var ul = draw.children[0];
    var startY = 0;
    // 剛觸碰到屏幕的時(shí)的手指信息
    var centerY = 0;

    // 用來(lái)記錄每次觸摸時(shí)上一次的偏移距離
    var maxDown = 50;

    // 設(shè)定一個(gè)最大向下滑動(dòng)的距離
    var maxUp = -(ul.offsetHeight - draw.offsetHeight + maxDown);

    // 求得一個(gè)最大向上滑動(dòng)的距離
    var maxUpBounce = 0;

    // 向上反彈值
    var maxDownBounce = -(ul.offsetHeight - draw.offsetHeight);

    // 向下反彈值
    // touchstart 時(shí),記錄手指在 Y 軸上的落點(diǎn)距離可視頂部距離
    ul.addEventListener('touchstart', function (e) {
        startY = e.changedTouches[0].clientY;

       })

    // touchmove 時(shí),記錄此時(shí)手指在 Y 軸上的落點(diǎn)距離可視頂部距離
    ul.addEventListener('touchmove', function (e) {

   // 清除過(guò)渡
        ul.style.transition = 'none';

   // 獲取差值
        var dy = e.changedTouches[0].clientY - startY;

   // 上次的滑動(dòng)距離加上本次的滑動(dòng)距離
        var tempY = centerY + dy;

   // 當(dāng)上次滑動(dòng)的距離加上本次滑動(dòng)的距離 大于 設(shè)定的最大向下距離的時(shí)候
        if (tempY > maxDown) {
            tempY = maxDown;
       }

   // 當(dāng)上次滑動(dòng)的距離加上本次滑動(dòng)的距離 小于 設(shè)定的最大向上距離的時(shí)候
        else if (tempY  maxUpBounce) {
    // 讓兩次滑動(dòng)的距離 等于 設(shè)置的值
            centerY = maxUpBounce;

    // 添加過(guò)渡
            ul.style.transition = 'transform .5s';
            ul.style.transform = 'translateY(' + centerY + 'px)';

        }

    // 兩次滑動(dòng)的距離 小于 設(shè)定的 向下 反彈值時(shí)
        else if (centerY 
聲明:所有內(nèi)容來(lái)自互聯(lián)網(wǎng)搜索結(jié)果,不保證100%準(zhǔn)確性,僅供參考。如若本站內(nèi)容侵犯了原著者的合法權(quán)益,可聯(lián)系我們進(jìn)行處理。
發(fā)表評(píng)論
更多 網(wǎng)友評(píng)論0 條評(píng)論)
暫無(wú)評(píng)論

返回頂部

主站蜘蛛池模板: 91成人高清在线播放| 日韩精品视频免费在线观看| 美女张开腿让男人桶的动态图| 久久精品国产亚洲av麻| 国产一级生活片| 大陆黄色a级片| 机机对机机的30分钟免费软件| 色哟哟在线网站| 49289.com| 丰满少妇被猛烈高清播放| 亚洲欧美日韩高清在线电影 | 男人女人真曰批视频大全免费观看| 永久黄色免费网站| 中文字幕一区二区三区四区| 亚洲第一黄色网| 国产99在线观看| 国产精品免费精品自在线观看| 我要看免费毛片| 欧美午夜电影在线观看| 精品少妇一区二区三区视频 | 亚洲女初尝黑人巨高清| 再深点灬舒服了灬太大| 国产四虎免费精品视频| 在线日韩理论午夜中文电影| 无翼乌全彩绅士知可子无遮挡| 欧美乱xxxxx| 水蜜桃视频在线观看免费| 羞羞漫画小舞被黄漫免费| 五月婷婷六月天| 97精品国产高清自在线看超| 两个人一上一下剧烈运动| 久久国产精品99精品国产| 亚洲成aⅴ人片在线观| 依依成人精品视频在线观看| 国产91小视频| 国产亚洲av片在线观看18女人| 国产精品一区二区电影| 天堂久久久久久中文字幕| 成人国产精品视频| 无套内射无矿码免费看黄| 日本高清有码视频|