原生js实现移动端滑动反弹效果

原生js实现移动端滑动反弹效果

纵向滑动效果演示
横向滑动效果演示
请在谷歌浏览器移动端模拟下查看

具体实现思路

触摸事件(touch event)可响应用户手指(或触控笔)对屏幕或者触控板操作,给基于触控的用户界面提供了可靠支持。

触摸事件接口是较为底层的 API,可为特定程序提供多点触控交互(比如双指手势)的支持。多点触控交互开始于一个手指(或触控笔)开始接触设备平面的时刻。随后其他手指也可触摸设备表面,并随意进行划动。当所有手指离开设备平面时,交互结束。整个交互期间,程序接收开始移动结束三个阶段的触摸事件。

触摸事件与鼠标事件类似,不同的是触摸事件还提供同一表面不同位置的同步触摸。TouchEvent 接口将当前所有活动的触摸点封装起来。Touch 接口表示单独一个触摸点,其中包含参考浏览器视角的相对坐标。

  • touchStart 当触点与触控设备表面接触时触发touchstart事件
  • touchmove 当触点在触控平面上移动时触发touchmove事件
  • touchend 当触点离开触控平面时触发touchend事件

详细知识可查看文档 触摸事件

我们需要用到的Touch事件触发的Event对象中的属性有changedTouchestarget

  • changedTouches 一个伪数组,装有触摸点信息列表
  • target 触摸点的DOM元素对象

实现细节

  1. 获取需要监听的元素对象 ( 这里是 ul 列表 )
var list = document.querySelector('.list')
复制代码
  1. 监听 touchStart 事件
var startX = 0; //初始化
list.addEventListener('touchstart', function(e) {
    e = e || window.event;
    e.preventDefault(); //阻止事件的默认事件
    startX = e.changedTouches[0].clientX;//触碰点距离屏幕左侧可视区的距离
})
复制代码
  1. 监听 touchmove 事件
var nav = document.querySelector('#nav'); //获取父容器
var centerX = 0; //初始移动距离
var maxLeft = 50; //允许左侧超出移动距离
var maxRight = -(list.offsetWidth - nav.offsetWidth + maxLeft) //允许右侧超出移动距离
list.addEventListener('touchmove', function(e) {
    e = e || window.event
    e.preventDefault()
    
    var dx = e.changedTouches[0].clientX - startX; //获取触碰点在屏幕上X轴移动的距离(即横向移动距离)
    var tempX = centerX + dx  //累计移动距离
    
    if (tempX > maxLeft) { //如果移动距离超过了左侧最大移动距离
    	tempX = maxLeft;  
    } else if (tempX < maxRight) { //如果移动距离超过了右侧最大移动距离
    	tempX = maxRight;
    }
    list.style.transform = 'translateX('+tempX+'px)'
    // ul.style.transform = `translateY(${tempY}px)`  //es6写法
})
复制代码
  1. 监听 touchend 事件
var maxLeftSlide = 0; //最大左侧显示距离
var maxRightSlide = -(list.offsetWidth - nav.offsetWidth + maxLeftSlide); //最大右侧显示距离
list.addEventListener('touchend', function(e) {
    e = e || window.event
    e.preventDefault()
    
    var dx = e.changedTouches[0].clientX - startX; //获取最终移动距离
    centerX = centerX + dx;  //累加上上次移动的距离
    if (centerX > maxLeftSlide) { 
    	centerX = maxLeftSlide; //回到初始显示位置
    } else if (centerX < maxRightSlide) {
    	centerX = maxRightSlide;
    }
    
    if(dx === 0) {
        var text = document.querySelector('#text')
    	text.innerHTML = e.target.innerHTML
    }
    list.style.transition = 'transfrom .5s' //添加过渡动画
    list.style.transform = 'translateX('+centerX+'px)'
    // ul.style.transform = `translateY(${centerY}px)`
})
复制代码

由于在 touchmove 事件中我们允许 ul 左右滑动可以超出 maxLeft 的距离,结束时我们就需要将距离回弹回来,这里通过添加一个 css3 过渡动画来达到回弹效果

各事件触发顺序 touchstart > click 在所有事件中我们都阻止了默认事件(这是为了防止浏览器的默认滑动事件影响)结果导致点击事件也不会触发,那么我们需要用 Touch 来模拟点击事件,其实就是当触发了 touchStart 且移动距离为 0 我们就可以认为是点击事件

  1. 小小的优化

移动到边界时会发现超出边界时会出现空白背景,这样体验有点不好。

给 ul 加 50px 横向填充,然后向左平移 50px ,最后修改一下最大左侧超出距离和最大左侧显示距离

css

.list {
    padding: 0 50px;
    transform: translateX(-50px);
}
复制代码

js

var maxLeft = 0;
var maxLeftSlide = -50;
复制代码

最终代码

Head部分

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
复制代码

Document结构(以横向滑动为例)

<nav id="nav">
    <ul class="list">
    	<li>最新</li>
    	<li>排行</li>
    	<li>国内</li>
    	<li>国际</li>
    	<li>社会</li>
    	<li>评论</li>
    	<li>深度</li>
    	<li>军事</li>
    	<li>历史</li>
    	<li>探索</li>
    	<li>图片</li>
    	<li>博客</li>
    	<li>媒体</li>
    	<li>视频</li>
    </ul>
</nav>
<p id="text"></p>
复制代码

CSS代码

* {
    margin: 0;
    padding: 0;
}

#nav {
    width: 100%;
    overflow: hidden;
}

#nav::after {
    content: '';
    display: block;
    visibility: hidden;
    height: 0;
    clear: both;
}

.list {
    float: left;
    white-space: nowrap;
    background: #1062ac;
    color: #CCCCCC;
    padding: 0 60px;
    transform: translateX(-50px);
}

.list>li {
    display: inline-block;
    padding: 10px;
}
复制代码

以上都还是简单一个目录导航布局,所有li元素进行左浮动,然后利用after伪类清楚浮动

js代码

var nav = document.querySelector('#nav');
var list = nav.children[0];
var newList = list.children[0];
var startX = 0;
var centerX = -50;
var maxLeft = 0;
var maxRight = -(list.offsetWidth - nav.offsetWidth + maxLeft);
var maxLeftSlide = -50;
var maxRightSlide = -(list.offsetWidth - nav.offsetWidth + maxLeftSlide);
list.addEventListener('touchstart', function(e) {
    e = e || window.event
    e.preventDefault()
    list.style.transition = 'none'
    startX = e.changedTouches[0].clientX;
})

list.addEventListener('touchmove', function(e) {
    e = e || window.event
    e.preventDefault()
    
    var dx = e.changedTouches[0].clientX - startX;
    var tempX = centerX + dx
    
    if (tempX > maxLeft) {
    	tempX = maxLeft;
    } else if (tempX < maxRight) {
    	tempX = maxRight;
    }
    list.style.transform = 'translateX('+tempX+'px)'
    // ul.style.transform = `translateY(${tempY}px)`
})

list.addEventListener('touchend', function(e) {
    e = e || window.event
    e.preventDefault()
    
    var dx = e.changedTouches[0].clientX - startX;
    centerX = centerX + dx;
    if (centerX > maxLeftSlide) {
    	centerX = maxLeftSlide;
    } else if (centerX < maxRightSlide) {
    	centerX = maxRightSlide;
    }
    
    if(dx === 0) {
        var text = document.querySelector('#text')
    	text.innerHTML = e.target.innerHTML
    }
    list.style.transition = 'transfrom .5s'
    list.style.transform = 'translateX('+centerX+'px)'
    // ul.style.transform = `translateY(${centerY}px)`
})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值