原生 JS 实现移动端 Touch 滑动反弹

什么是 Touch滑动?就是类似于 PC端的滚动事件,但是在移动端是没有滚动事件的,所以就要用到 Touch事件结合 js去实现,效果如下:

1. 准备工作

什么是移动端的 Touch事件?在移动端 Touch事件可以细分成三种,分别是: touchstarttouchmovetouchend,并且 touch事件必须要用 addEventListener去监听。

  • touchStart当手指触碰到屏幕的时候触发
  • touchmove当手指在屏幕上不断移动的时候触发
  • touchend当手指离开屏幕的时候触发

Touch 事件触发的 Event 对象

// 手指触碰到屏幕时触发
element.addEventListener(‘touchstart‘,function (e){
// 打印的事件对象
    console.log(e);
})

changedTouchestargetTouchestouches都是伪数组,里面装的是手指列表。

三种返回对象的区别

其实这三种返回的对象,都是表示用户触摸事件时的手指信息,之所以是一个伪数组,是因为有可能出现多指同时触摸,但是在实际工作中一般不去考虑多指的情况。而它们唯一的区别就是在 touchstarttouchmove事件的时候, changedTouchestargetTouchestouches都能获取到手指的信息,但是在 touchend事件的时候, targetTouchestouches对象是不能返回离开屏幕时的手指信息,只有 changedTouches对象能返回。

有哪些手指信息?

我们可以看下上面的图片,在 changedTouche[0]中,有些值:

clientX:74  // 触摸点相对于浏览器的 viewport 左边缘的 x 坐标,不会包括左边的滚动离。
clientY:73    // 触摸点相对于浏览器的 viewport 上边缘的 Y 坐标,不会包括上边的滚动离。
screenX:2202    // 触摸点相对于屏幕左边缘的 x 坐标。
screenY:327    // 触摸点相对于屏幕上边缘的 Y 坐标。
pageX:65     // 触摸点相对于 document 的左边缘的 x 坐标,包括左边的滚动距离
pageY:18      // 触摸点相对于 document 的上边缘的 Y 坐标,包括左边的滚动距离

2. 基本结构

此案例模拟的是移动端的一种滑动菜单效果。

HTML部分

<aside class="main">
    <div class="draw" id="draw">
        <ul>
            <li style="background:orange">列表一</li>
            <li style="background:yellowgreen">列表二</li>
            <li style="background:yellow">列表三</li>
            <li style="background:cyan">列表四</li>
            <li style="background:orangered">列表五</li>
            <li style="background:pink">列表六</li>
            <li style="background:red">列表七</li>
            <li style="background:purple">列表八</li>
            <li style="background:violet">列表九</li>
            <li style="background:brown">列表十</li>
        </ul>
    </div>
</aside>

css部分

在列表的父盒子上设定一个 overflow:hidden属性,使超出盒子部分的列表暂时隐藏掉,后面会通过 js去实现滑动。

* {
    margin:0;
    padding:0
}
html,body {
    width:100 %
}
aside {
    height:100 %;
    width:100 %
}
. draw {
    width:60px;
    height:500px;
    border:2px solid #ccc;
    overflow:hidden;
    position:fixed;
    left:10px;
    top:50 %;
    transform:translateY(- 50 %)
}
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
}

效果图

3. 首次滑动

手指触摸到列表向下滑动的时候,列表应该跟着向下滑动,当手指离开屏幕的时候,列表应该停在滑动的位置。这里就会用到上面准备阶段的知识点了,不明白的可以参考上面的概念。

实现原理

  1. touchstart的时候,获取手指触摸的落点 A,通过这个点对象里面的 clientY属性,获取距离顶部可视区的距离;
  2. touchmove的时候,获取手指的点 B,同样的获取移动时距离顶部可视区的距离;
  3. touchmove的时候,还要做另一件事情,就是获取两点的差值( B.clientY-A.clientY),将这个差值动态赋值给 ul, ul只需要设置向 Y轴方向偏移这个距离,就能实现列表随手指滑动

先来张示意图,怎么通过 js 让列表滑动起来

示例代码

var draw = document.querySelector(‘#draw‘);
var ul = draw.children[0];
// touchstart 时,记录手指在 Y 轴上的落点距离可视顶部距离
var startY =0 ul.addEventListener(‘touchstart‘,function(e){
    startY = e.changedTouches[0].clientY;
})
// touchmove 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
ul.addEventListener(‘touchmove‘,function(e){
    // 获取差值
    var dy = e.changedTouches[0].clientY - startY;
    // 设置 ul 在 Y 轴上的偏移
    ul.style.transform =‘translateY(‘+ dy +‘px)‘;
})

效果图

 

4. 再次滑动

上面的效果图,细心的朋友可能已经发现了问题,在第一次的时候,得到了我们想要的效果,但是在第二次的时候,我们继续向下移动了一段距离,但是 ul并没有接着第一次的位置继续向下,而是瞬间跳了上去。

问题分析

虽然第二次是继续向下移动了一段距离,但是触摸结束后,最终是将此时的差值,重新赋值给了 ulY轴偏移,所以视觉上“跳了上去”。

解决方法:

每一次滑动结束之后,都应该记录下此次滑动的距离,与之前的进行累加,待下一次滑动的时候, ulY轴的偏移值应该是之前的距离加上本次滑动的距离。

  • 新增 touchend事件,在该事件里同样的可以获取到本次滑动的距离,将它与上一次的距离相加,赋值给一个全局变量;
  • 在 touchmove事件里有点小改动,就是在给 ul设置偏移值的时候,除了本次滑动的差值还要加上这个上一次的值;

示意图:

示例代码

var draw=document.querySelector(‘#draw‘);
var ul=draw.children[0];

var startY=0// 刚触碰到屏幕的时的手指信息
var centerY=0// 用来记录每次触摸时上一次的偏移距离

// touchstart 时,记录手指在 Y 轴上的落点距离可视顶部距离
ul.addEventListener(‘touchstart‘,function(e){
    startY=e.changedTouches[0].clientY;
})

// touchmove 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
ul.addEventListener(‘touchmove‘,function(e){
// 获取差值
    var dy=e.changedTouches[0].clientY-startY;
// 上次的滑动距离加上本次的滑动距离
    var tempY=centerY+dy;
// 设置 ul 在 Y 轴上的偏移
    ul.style.transform=‘translateY(‘+tempY+‘px)‘;
})

// touchend 时,记录此时手指在 Y 轴上的落点距离可视顶部距离
ul.addEventListener(‘touchend‘,function(e){
    // 获取差值
  var dy=e.changedTouches[0].clientY-startY;
    // 记录移动的距离
  centerY=centerY+dy;
})

效果图

5. 限制滑动区间

到上面一步,我们已经可以实现列表的滑动了,但是也存在一个问题,就是向上或者向下的时候没有限制,上下可以无限的滑动,甚至再用点力,就看不到列表了。为了美观和实用,这样肯定不行的,需要给它设定一个区间,设定向上或者向下最多只能留白多少。

限制向下滑动最大区间

设定向下最大区间的值比较简单,直接设定一个值,当上一次滑动的距离加上本次滑动的距离大于这个值的时候,就不让它再继续往下滑了,让他直接等于这个设定的值。

示例代码

var maxDown = 50; // 设定一个最大向下滑动的距离

// touchmove 时,记录此时手指在 Y 轴上的落点距离可视顶部距离

ul.addEventListener(‘touchmove‘, function (e) {

 // 获取差值

 var dy = e.changedTouches[0].clientY - startY;

 // 上次的滑动距离加上本次的滑动距离

 var tempY = centerY + dy;

 // 当上次滑动的距离加上本次滑动的距离 大于 设定的最大向下距离的时候

 if (tempY > maxDown) {

   // 直接让偏移的值 等于这个设定值

   tempY = maxDown;

 }

 // 设置 ul 在 Y 轴上的偏移

 ul.style.transform = ‘translateY(‘ + tempY + ‘px)‘;

})

限制向上滑动最大区间

向上滑动时,当 ul的底部距盒子底部的距离大于设定值的时候,不让其继续向上滑动,关键是这个值怎么去判断?

求出向上滑动最大值

 

注意:因为 ul是向上滑动的,所以求得的距离前面要加上一个负号( -

示例代码

// 设定一个最大向下滑动的距离

var maxDown = 50;

// 求得一个最大向上滑动的距离

var maxUp = -(ul.offsetHeight - draw.offsetHeight + maxDown);

// touchmove 时,记录此时手指在 Y 轴上的落点距离可视顶部距离

ul.addEventListener(‘touchmove‘, function (e) {

 // 获取差值

 var dy = e.changedTouches[0].clientY - startY;

 // 上次的滑动距离加上本次的滑动距离

 var tempY = centerY + dy;

 // 当上次滑动的距离加上本次滑动的距离 大于 设定的最大向下距离的时候

 if (tempY > maxDown) {

   tempY = maxDown;

 }

 // 当上次滑动的距离加上本次滑动的距离 小于 设定的最大向上距离的时候

 else if (tempY < maxUp) {

   // 直接让偏移的值 等于这个设定值

   tempY = maxUp;

 }

 // 设置 ul 在 Y 轴上的偏移

 ul.style.transform = ‘translateY(‘ + tempY + ‘px)‘;

})

效果图

 

认真观察上图,虽然成功的设置了最大滑动区间,但是你有没有发现,一直往一个方向滑动的时候,虽然列表不会继续往下滑动,但是接着往相反方向滑动的时候,感觉列表滑不动,需要滑一段距离后,列表才会跟着走,这是为什么呢?因为滑动的过程 centerY是一直变的,列表虽然视觉上不动了,但是在 touchend事件的时候,它的 centerY值一直在累加。解决方法请往下看。

6. 设定反弹区间

“滑动反弹”,这里的反弹是本篇文章的最后一步,上面说到的问题,就在这里解决。因为每一次触发 touchend事件的时候, centerY值就累加一次,所以需要在 touchend事件里做判断。我们设定一个反弹区间,就是当 centerY的值大于或者小于某个值的时候,让它触发反弹。

设定向上反弹值

向上的值比较简单,设置成“ 0”。为什么是“ 0”呢?我们限定只要手指离开时,上一次的滑动距离加上本次的距离 >0的时候,就让它触发反弹,并且反弹回 0点的位置,也就是两次滑动的距离和 =0

示例代码

// 向上反弹

var maxUpBounce = 0;

// touchend 时,记录此时手指在 Y 轴上的落点距离可视顶部距离

ul.addEventListener(‘touchend‘, function (e) {

 // 获取差值

 var dy = e.changedTouches[0].clientY - startY;

 // 记录移动的距离

 centerY = centerY + dy;

 // 两次滑动的距离 大于 设定的 向上 反弹值时

 if (centerY > maxUpBounce) {

   // 让两次滑动的距离 等于 设置的值

   centerY = maxUpBounce;

   // 添加过渡

   ul.style.transition = ‘transform .5s‘;

   ul.style.transform = ‘translateY(‘ + centerY + ‘px)‘;

 }

})

设定向下反弹值

向下的值其实跟之前求滑动区间差不多,我们参考下图,当列表向上滑动,滑动到列表底部的时候,只要此时再向上滑动,就让它向下反弹。向下反弹值就是 -(ul.offsetHeight-draw.offsetHeight),只要滑动的差值小于这个设定值,就让它向下反弹,并且反弹回设定值的位置。

// 向上反弹值

var maxUpBounce = 0;

// 向下反弹值

var maxDownBounce = -(ul.offsetHeight - draw.offsetHeight);

// touchend 时,记录此时手指在 Y 轴上的落点距离可视顶部距离

ul.addEventListener(‘touchend‘, function (e) {

 // 获取差值

 var dy = e.changedTouches[0].clientY - startY;

 // 记录移动的距离

 centerY = centerY + dy;

 // 两次滑动的距离 大于 设定的 向上 反弹值时

 if (centerY > maxUpBounce) {

   // 让两次滑动的距离 等于 设置的值

   centerY = maxUpBounce;

   // 添加过渡

   ul.style.transition = ‘transform .5s‘;

   ul.style.transform = ‘translateY(‘ + centerY + ‘px)‘;

 }

 // 两次滑动的距离 小于 设定的 向下 反弹值时

 else if (centerY < maxDownBounce) {

   // 让两次滑动的距离 等于 设置的值

   centerY = maxDownBounce;

   // 添加过渡

   ul.style.transition = ‘transform .5s‘;

   ul.style.transform = ‘translateY(‘ + centerY + ‘px)‘;

 }

})

注意:在 touchend事件的时候,给列表添加了 transition属性才会有反弹的效果,但是,下一次滑动的时候, touchmove事件的时候,这个属性还存在,所以就会出现滑动的时候有顿挫感,所以在 touchmove事件的时候,一进来就清一下过渡 ul.style.transition=‘none‘;

完成后效果图

7. 完整代码

<!DOCTYPE html>

<html lang="en">

<head>

 <meta charset="UTF-8">

 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">

 <meta http-equiv="X-UA-Compatible" content="ie=edge">

 <title>移动端 Touch 滑动反弹</title>

 <style>

   /* 样式初始化 */

   * {

     margin: 0;

     padding: 0;

   }

   html,

   body {

     width: 100%;

   }

   aside {

     height: 100%;

     width: 100%;

   }

   /* 列表的父盒子,限制宽高 */

   /* 注意设置overflow: hidden;样式后,超出这个盒子的ul将不会显示 */

   .draw {

     width: 60px;

     height: 500px;

     border: 2px solid #ccc;

     overflow: hidden;

     position: fixed;

     left: 10px;

     top: 50%;

     transform: translateY(-50%);

   }

   /* li 设置了浮动, 所以 ul 要清除浮动 */

   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;

   }

 </style>

</head>

<aside class="main">

 <div class="draw" id="draw">

   <ul>

     <li style="background:orange">列表一</li>

     <li style="background:yellowgreen">列表二</li>

     <li style="background:yellow">列表三</li>

     <li style="background:cyan">列表四</li>

     <li style="background:orangered">列表五</li>

     <li style="background:pink">列表六</li>

     <li style="background:red">列表七</li>

     <li style="background:purple">列表八</li>

     <li style="background:violet">列表九</li>

     <li style="background:brown">列表十</li>

   </ul>

 </div>

</aside>

<body>

 <script>

   var draw = document.querySelector(‘#draw‘);

   var ul = draw.children[0];

   var startY = 0; // 刚触碰到屏幕的时的手指信息

   var centerY = 0; // 用来记录每次触摸时上一次的偏移距离

   var maxDown = 50; // 设定一个最大向下滑动的距离

   var maxUp = -(ul.offsetHeight - draw.offsetHeight + maxDown); // 求得一个最大向上滑动的距离

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

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

   // touchstart 时,记录手指在 Y 轴上的落点距离可视顶部距离

   ul.addEventListener(‘touchstart‘, function (e) {

     startY = e.changedTouches[0].clientY;

   })

   // touchmove 时,记录此时手指在 Y 轴上的落点距离可视顶部距离

   ul.addEventListener(‘touchmove‘, function (e) {

     // 清除过渡

     ul.style.transition = ‘none‘;

     // 获取差值

     var dy = e.changedTouches[0].clientY - startY;

     // 上次的滑动距离加上本次的滑动距离

     var tempY = centerY + dy;

     // 当上次滑动的距离加上本次滑动的距离 大于 设定的最大向下距离的时候

     if (tempY > maxDown) {

       tempY = maxDown;

     }

     // 当上次滑动的距离加上本次滑动的距离 小于 设定的最大向上距离的时候

     else if (tempY < maxUp) {

       tempY = maxUp;

     }

     // 设置 ul 在 Y 轴上的偏移

     ul.style.transform = ‘translateY(‘ + tempY + ‘px)‘;

   })

   // touchend 时,记录此时手指在 Y 轴上的落点距离可视顶部距离

   ul.addEventListener(‘touchend‘, function (e) {

     // 获取差值

     var dy = e.changedTouches[0].clientY - startY;

     // 记录移动的距离

     centerY = centerY + dy;

     // 两次滑动的距离 大于 设定的 向上 反弹值时

     if (centerY > maxUpBounce) {

       // 让两次滑动的距离 等于 设置的值

       centerY = maxUpBounce;

       // 添加过渡

       ul.style.transition = ‘transform .5s‘;

       ul.style.transform = ‘translateY(‘ + centerY + ‘px)‘;

     }

     // 两次滑动的距离 小于 设定的 向下 反弹值时

     else if (centerY < maxDownBounce) {

       // 让两次滑动的距离 等于 设置的值

       centerY = maxDownBounce;

       // 添加过渡

       ul.style.transition = ‘transform .5s‘;

       ul.style.transform = ‘translateY(‘ + centerY + ‘px)‘;

     }

   })

 </script>

</body>

</html>

原文地址:https://www.cnblogs.com/xiaoqi2018/p/10293470.html

时间: 2024-09-30 20:55:07

原生 JS 实现移动端 Touch 滑动反弹的相关文章

原生js实现移动端Touch轮播图的方法步骤

Touch 轮播图 touch轮播图其实就是通过手指的滑动,来左右切换轮播图,下面我们通过一个案例,来实现下. 1. html 结构 结构上,还是用ul.li来存放轮播图片,ol.li来存放轮播小圆点: 2. 样式初始化 html的一些标签,都会有一些默认样式,比如body标签默认是有一个边距的,为了不影响美观,我们需要清除掉. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 /* 清除标签默认边距 */ body,ul,li,ol,im

原生js实现一个DIV的碰撞反弹运动

 原生js实现一个DIV的碰撞反弹运动: 关键在于DIV的边界检测,进而改变运动方向,即可实现碰撞反弹效果. <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> *{margin:0;} div{height:100px;width:100px;back

移动端touch滑动方向的判断

var slider = {  //判断设备是否支持touch事件     touch:('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch,    //事件  events:{   slider:document,   handleEvent:function(event){    if(event.type == 'touchstart'){     this.st

纯原生js移动端图片压缩上传插件

前段时间,同事又来咨询一个问题了,说手机端动不动拍照就好几M高清大图,上传服务器太慢,问问我有没有可以压缩图片并上传的js插件,当然手头上没有,别慌,我去网上搜一搜. 结果呢,呵呵...诶~又全是基于jquery.zepto的(这句话似乎吐槽次数太多了...),然后我也就不吐槽了, 然后当然是自己做了,先上图: 纯原生js的移动端图片压缩上传插件,不依赖任何库 用法 在html页面中引入input标签,通过自定义属性data-LUploader绑定点击触发的标签id,写法如下: <div cla

&#31227;&#21160;&#31471;&#19978;&#19979;&#28369;&#21160;&#20107;&#20214;&#20043;--&#22353;&#29241;&#30340;touch.js

转:http://blog.csdn.net/minidrupal/article/details/39611605?utm_source=tuicool&utm_medium=referral 移动端页面的盛行,微信的便利的页面推广等等,让越来越多的css3效果和html5在手机端大放异彩。 于是乎,各式各样的简约酷炫的html5页面层出不穷,最多的就是视差滚动+css3动画。 接下来就说说自己在搞这些页面里面碰到的一个小问题-------zepto.js里面,坑爹的touch.js的上下滑动

移动端上下滑动事件之--坑爹的touch.js

原文   http://blog.csdn.net/minidrupal/article/details/39611605 移动端页面的盛行,微信的便利的页面推广等等,让越来越多的css3效果和html5在手机端大放异彩. 于是乎,各式各样的简约酷炫的html5页面层出不穷,最多的就是视差滚动+css3动画. 接下来就说说自己在搞这些页面里面碰到的一个小问题-------zepto.js里面,坑爹的touch.js的上下滑动( swipe )事件失效. 在举例之前,先科普一下如何在pc端,查看h

原生js移动端touch事件实现上拉加载更多

大家都知道jQuery里没有touch事件,所以在移动端使用原生js实现上拉加载效果还是很不错的,闲话不多说,代码如下: 1 //获取要操作的元素 2 var objSection = document.getElementsByTagName("div")[0]; 3 //给元素绑定监听事件 个人习惯把监听事件写在一块 4 objSection.addEventListener("touchstart", touchStart, false); 5 objSect

告别组件之教你使用原生js和css写移动端轮播图

在工作中由于项目需要要写一个轮播图,本想使用组件直接调用实现快速开发,但是一想到自己经常使用组件但是让自己手写的话确实一点都不会. 一个不会手写组件的前端程序员不是一个好程序员!于是打算自己手写一个. 老规矩,首先看一下最终效果,这个最终可以实现定时自动播放,触摸滑动,手动修改下面横条效果等功能. 项目中使用到的HTML代码如下 <div class="banner"> <ul class="clearfix"> <li><

实现移动端touch事件的横向滑动列表效果

要实现手机端横向滑动效果并不难,了解实现的原理及业务逻辑就很容易实现.原理:touchstart(手指按下瞬间获取相对于页面的位置)-->touchmove(手指移动多少,元素相应移动多少). 接下来讲讲实现逻辑: 其实就是手指拖动列表向哪个方向移动多少像素,并设置左右拖动的边界值. 附上代码及注释: <div id="common_wrap" class="common-wrap"> <h4 class="common-kit__