基于jQuery的左滑出现删除按钮的示例


Posted in jQuery onAugust 29, 2017

最近在做项目的时候遇到了个需求,在网页上实现类似于QQ会话列表那样子的左滑出现删除按钮的效果,于是尝试着写了一个,写出来与大家交流分享,大神勿喷。

基本要求

由于我们是在做一个跨平台的APP,里面部分界面其实就是WebView加载的网页,因此需要使用网页实现这样的效果:往左滑动时,显示删除按钮,再往右滑,隐藏删除按钮。

成品示例图

额,先上图吧。下面分别是在PC浏览器里和在Mobile浏览器里的效果。

PC浏览器

基于jQuery的左滑出现删除按钮的示例

手机浏览器

基于jQuery的左滑出现删除按钮的示例

实现思路

为了说明我的实现思路,做了两个图来辅助说明。

首先,请看图1。在图中,我们设置每一行的宽度超过浏览器的宽度,其超出的部分就是放置按钮的区域。由于超出了浏览器的最大宽度,因此按钮区域此时是不可见的,只能显示左侧的常规信息部分。

基于jQuery的左滑出现删除按钮的示例

图1 普通状态

接下来,我们监听左侧常规信息区域,监听滑动事件(具体如何监听先不考虑)。当我们监听到左滑事件时,我们让相应行左偏移,让按钮显示出来,同时左侧超出的部分被遮挡(请看图2)。

基于jQuery的左滑出现删除按钮的示例

图2 左滑状态

当我们右滑时,我们再让相应行回复到左偏移为0的时候就行了。

关键实现方式

对于左滑和右滑,我们通过设定常规信息区域的marin-left来实现,当设定margin-left为负值时,实现左滑,当再次设定margin-left为0时,实现右滑。

对于滑动事件监听,通过监听鼠标(手指)按下和抬起来实现,根据两点的X坐标的差值的正负判定是右滑还是左滑。

完整代码

需要注意的是,我在测试的时候用的是chrome的普通模式和mobile模拟器模式,发现两种模式下监听是不一样的,因此我写了两种监听,这样至少有一种会执行。也许有其他更好的适配方法,但不作为此处的重点。当然也欢迎大家赐教。

至于代码部分,用了jQuery,其实不用也没啥问题的,动画滑动和监听都可以用纯js写,但是由于这不是这里的重点,那么为什么不用jQuery呢?成功者站在巨人的肩膀上,而且咱也没有jQuery写的好 (。・`ω´・)

2015/11/13更新

有位同学提出说代码在QQ手机浏览器和Opera手机浏览器等中没有滑动效果,找了一下大概是帖子里说的原因,于是根据帖子里的提示以及那位同学的一个大神同学的提示,做了一下修改。主要是在touchmove事件中根据横纵坐标位移来判断是否阻止默认事件,如下:

// 横向位移大于纵向位移,阻止纵向滚动
if (Math.abs(delta.x) > Math.abs(delta.y)) {
  event.preventDefault();
}

2016/02/25更新

qq_25558115同学提到:“如果再能给大家提供出只能有一条记录可以左滑, 倘若滑动其他的记录,则有左滑记录的要回到原位”。于是进行了简单实现。主要思路如下:

// 用一个变量记录上一次左滑的对象
var lastLeftObj;

// 在左滑发生的时候,判定上一个左滑的对象是否存在,若存在,且不是当前被左滑的对象,则将其右滑
// 同时,记录新的左滑对象
// 在右滑发生时,将上一个左滑对象清空
if (左滑) {
  pressedObj左滑
  lastLeftObj && lastLeftObj != pressedObj && lastLeftObj右滑
  lastLeftObj = pressedObj; // 记录上一个左滑的对象
} else if (右滑) {
  pressedObj右滑
  lastLeftObj = null; // 清空上一个左滑的对象
}

2016/09/06更新

根据马灿发同学提出的bug进行修改:

右滑时进行判断,仅当要右滑的对象(pressedObj)是上一次左滑的对象(lastLeftObj)时才将对象右滑并清空lastLeftObj。

if (pressedObj == lastLeftObj) {...}

根据girlyougo同学的提议,添加“在除本行外的其他区域点击时均复位当前左滑按钮”的功能。思路为在滑动结束时,判定pressedObj!=lastLeftObj,即可知点击/滑动的对象为其他对象:

// 点击除当前左滑对象之外的任意其他位置
if (lastLeftObj && pressedObj != lastLeftObj) {
  $(lastLeftObj).animate({marginLeft:"0"}, 500); // 右滑
  lastLeftObj = null; // 清空上一个左滑的对象
}

实际上,在添加了上述功能之后,前面提到的bug不存在了。不过此处保留了消除bug的那部分代码。

更新后的完整代码如下:

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>左划出现删除按钮,右滑隐藏</title>
<script type="text/javascript" src="jquery-1.11.2.min.js"></script>
<script type="text/javascript">
$(document).ready(function(e) {
  // 设定每一行的宽度=屏幕宽度+按钮宽度
  $(".line-scroll-wrapper").width($(".line-wrapper").width() + $(".line-btn-delete").width());
  // 设定常规信息区域宽度=屏幕宽度
  $(".line-normal-wrapper").width($(".line-wrapper").width());
  // 设定文字部分宽度(为了实现文字过长时在末尾显示...)
  $(".line-normal-msg").width($(".line-normal-wrapper").width() - 280);

  // 获取所有行,对每一行设置监听
  var lines = $(".line-normal-wrapper");
  var len = lines.length; 
  var lastX, lastXForMobile;

  // 用于记录被按下的对象
  var pressedObj; // 当前左滑的对象
  var lastLeftObj; // 上一个左滑的对象

  // 用于记录按下的点
  var start;

  // 网页在移动端运行时的监听
  for (var i = 0; i < len; ++i) {
    lines[i].addEventListener('touchstart', function(e){
      lastXForMobile = e.changedTouches[0].pageX;
      pressedObj = this; // 记录被按下的对象 

      // 记录开始按下时的点
      var touches = event.touches[0];
      start = { 
        x: touches.pageX, // 横坐标
        y: touches.pageY // 纵坐标
      };
    });

    lines[i].addEventListener('touchmove',function(e){
      // 计算划动过程中x和y的变化量
      var touches = event.touches[0];
      delta = {
        x: touches.pageX - start.x,
        y: touches.pageY - start.y
      };

      // 横向位移大于纵向位移,阻止纵向滚动
      if (Math.abs(delta.x) > Math.abs(delta.y)) {
        event.preventDefault();
      }
    });

    lines[i].addEventListener('touchend', function(e){
      if (lastLeftObj && pressedObj != lastLeftObj) { // 点击除当前左滑对象之外的任意其他位置
        $(lastLeftObj).animate({marginLeft:"0"}, 500); // 右滑
        lastLeftObj = null; // 清空上一个左滑的对象
      }
      var diffX = e.changedTouches[0].pageX - lastXForMobile;
      if (diffX < -150) {
        $(pressedObj).animate({marginLeft:"-132px"}, 500); // 左滑
        lastLeftObj && lastLeftObj != pressedObj && 
          $(lastLeftObj).animate({marginLeft:"0"}, 500); // 已经左滑状态的按钮右滑
        lastLeftObj = pressedObj; // 记录上一个左滑的对象
      } else if (diffX > 150) {
       if (pressedObj == lastLeftObj) {
        $(pressedObj).animate({marginLeft:"0"}, 500); // 右滑
        lastLeftObj = null; // 清空上一个左滑的对象
       }
      }
    });
  }

  // 网页在PC浏览器中运行时的监听
  for (var i = 0; i < len; ++i) {
    $(lines[i]).bind('mousedown', function(e){
      lastX = e.clientX;
      pressedObj = this; // 记录被按下的对象
    });

    $(lines[i]).bind('mouseup', function(e){
      if (lastLeftObj && pressedObj != lastLeftObj) { // 点击除当前左滑对象之外的任意其他位置
        $(lastLeftObj).animate({marginLeft:"0"}, 500); // 右滑
        lastLeftObj = null; // 清空上一个左滑的对象
      }
      var diffX = e.clientX - lastX;
      if (diffX < -150) {
        $(pressedObj).animate({marginLeft:"-132px"}, 500); // 左滑
        lastLeftObj && lastLeftObj != pressedObj && 
          $(lastLeftObj).animate({marginLeft:"0"}, 500); // 已经左滑状态的按钮右滑
        lastLeftObj = pressedObj; // 记录上一个左滑的对象
      } else if (diffX > 150) {
       if (pressedObj == lastLeftObj) {
        $(pressedObj).animate({marginLeft:"0"}, 500); // 右滑
        lastLeftObj = null; // 清空上一个左滑的对象
       }
      }
    });
  }
});
</script>
<style type="text/css">
* { margin: 0; padding: 0; }
.line-wrapper { width: 100%; height: 144px; overflow: hidden; font-size: 28px; border-bottom: 1px solid #aaa; }
.line-scroll-wrapper { white-space: nowrap; height: 144px; clear: both; }
.line-btn-delete { float: left; width: 132px; height: 144px; }
.line-btn-delete button { width: 100%; height: 100%; background: red; border: none; font-size: 24px; font-family: 'Microsoft Yahei'; color: #fff; }
.line-normal-wrapper { display: inline-block; line-height: 100px; float: left; padding-top: 10px; padding-bottom: 10px; }
.line-normal-icon-wrapper { float: right; width: 120px; height: 120px; margin-right: 12px; }
.line-normal-icon-wrapper img { width: 120px; height: 120px; }
.line-normal-avatar-wrapper { width: 100px; height: 124px; float: left; margin-left: 12px; }
.line-normal-avatar-wrapper img { width: 92px; height: 92px; border-radius: 60px; }
.line-normal-left-wrapper { float: left; overflow: hidden; }
.line-normal-info-wrapper { float: left; margin-left: 10px; }
.line-normal-user-name { height: 28px; line-height: 28px; color: #4e4e4e; margin-top: 7px; }
.line-normal-msg { height: 28px; line-height: 28px; overflow:hidden; text-overflow:ellipsis; color: #4e4e4e; margin-top: 11px; }
.line-normal-time { height: 28px; line-height: 28px; color: #999; margin-top: 11px; }
</style>
</head>
<body>
<div class="line-wrapper">
 <div class="line-scroll-wrapper">
  <div class="line-normal-wrapper">
   <div class="line-normal-left-wrapper">
    <div class="line-normal-avatar-wrapper"><img src="1.jpg" /></div>
    <div class="line-normal-info-wrapper">
     <div class="line-normal-user-name">蜡笔小新</div>
     <div class="line-normal-msg">在同行的小伙伴中提到了你</div>
     <div class="line-normal-time">1分钟前</div>
    </div>
   </div>
   <div class="line-normal-icon-wrapper"><img src="5.jpg"/></div>
  </div>
  <div class="line-btn-delete"><button>删除</button></div>
 </div>
</div>
<div class="line-wrapper">
 <div class="line-scroll-wrapper">
  <div class="line-normal-wrapper">
   <div class="line-normal-left-wrapper">
    <div class="line-normal-avatar-wrapper"><img src="2.jpg" /></div>
    <div class="line-normal-info-wrapper">
     <div class="line-normal-user-name">乔巴</div>
     <div class="line-normal-msg">你看不到我哦</div>
     <div class="line-normal-time">1分钟前</div>
    </div>
   </div>
   <div class="line-normal-icon-wrapper"><img src="6.jpg"/></div>
  </div>
  <div class="line-btn-delete"><button>删除</button></div>
 </div>
</div>
<div class="line-wrapper">
 <div class="line-scroll-wrapper">
  <div class="line-normal-wrapper">
   <div class="line-normal-left-wrapper">
    <div class="line-normal-avatar-wrapper"><img src="3.jpg" /></div>
    <div class="line-normal-info-wrapper">
     <div class="line-normal-user-name">贱行贱远</div>
     <div class="line-normal-msg">回忆里想起模糊的小时候,云朵漂浮在蓝蓝的天空,那时的你说,要和我手牵手,一起走到时间的尽头</div>
     <div class="line-normal-time">1分钟前</div>
    </div>
   </div>
   <div class="line-normal-icon-wrapper"><img src="7.jpg"/></div>
  </div>
  <div class="line-btn-delete"><button>删除</button></div>
 </div>
</div>
<div class="line-wrapper">
 <div class="line-scroll-wrapper">
  <div class="line-normal-wrapper">
   <div class="line-normal-left-wrapper">
    <div class="line-normal-avatar-wrapper"><img src="4.png" /></div>
    <div class="line-normal-info-wrapper">
     <div class="line-normal-user-name">小黄人</div>
     <div class="line-normal-msg">哈哈哈哈哈……暑假来看小黄人电影哦~哈哈哈……</div>
     <div class="line-normal-time">1分钟前</div>
    </div>
   </div>
   <div class="line-normal-icon-wrapper"><img src="8.jpg"/></div>
  </div>
  <div class="line-btn-delete"><button>删除</button></div>
 </div>
</div>
</body>
</html>

总结

代码还比较粗糙,存在很多bug,也有些地方不是那么绝对。比如当我按下时是在第一条记录,然后抬起时是在第二条记录,那么这时候滑动将是第一条记录。但是这个看具体需求了,如果你觉得滑动的对象应该以按下去时的对象为准的话,那就不管在哪抬起都滑动那个按下时的对象;如果你觉得滑动的对象应该是抬起时的对象,那也没问题,或者你觉得按下和抬起时不是同一个对象就不滑动任何对象那也行。总之,看需求。

源码请戳:源码下载

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

jQuery 相关文章推荐
jQuery日程管理控件glDatePicker用法详解
Mar 29 jQuery
jQuery实现菜单栏导航效果
Aug 15 jQuery
jQuery选择器中的特殊符号处理方法
Sep 08 jQuery
jQuery实现可兼容IE6的滚动监听功能
Sep 20 jQuery
基于jQuery实现定位导航位置效果
Nov 15 jQuery
jQuery zTree 异步加载添加子节点重复问题
Nov 29 jQuery
jquery写出PC端轮播图实例
Jan 26 jQuery
jQuery实现的鼠标响应缓冲动画效果示例
Feb 13 jQuery
jQuery替换节点元素的操作方法
Mar 18 jQuery
jQuery实现图片简单轮播功能示例
Aug 13 jQuery
详解jQuery中的getAll()和cleanData()
Apr 15 jQuery
Jquery动态列功能完整实例
Aug 30 jQuery
jQuery实现简单的计时器功能实例分析
Aug 29 #jQuery
jQuery动态添加.active 实现导航效果代码思路详解
Aug 29 #jQuery
jQuery实现base64前台加密解密功能详解
Aug 29 #jQuery
jquery在vue脚手架中的使用方式示例
Aug 29 #jQuery
jQuery 实现鼠标画框并对框内数据选中的实例代码
Aug 29 #jQuery
jQuery实现锚点向下平滑滚动特效示例
Aug 29 #jQuery
jQuery 1.9版本以上的浏览器判断方法代码分享
Aug 28 #jQuery
You might like
php获取操作系统语言代码
2013/11/04 PHP
分享下页面关键字抓取components.arrow.com站点代码
2014/01/30 PHP
php实现天干地支计算器示例
2014/03/14 PHP
PHP中常用的字符串格式化函数总结
2014/11/19 PHP
php curl 上传文件代码实例
2015/04/27 PHP
javascript textarea光标定位方法(兼容IE和FF)
2011/03/12 Javascript
jquery1.10给新增元素绑定事件的方法
2014/03/06 Javascript
javascript 实现map集合
2015/04/03 Javascript
Bootstrap按钮组件详解
2016/04/26 Javascript
JavaScript中split与join函数的进阶使用技巧
2016/05/03 Javascript
Boostrap实现的登录界面实例代码
2016/10/09 Javascript
JavaScript中使用参数个数实现重载功能
2017/09/01 Javascript
JavaScript实现点击出现图片并统计点击次数功能示例
2018/07/23 Javascript
JavaScript HTML DOM元素 节点操作汇总
2019/07/29 Javascript
Javascript 对象(object)合并操作实例分析
2019/07/30 Javascript
Vue-CLI项目中路由传参的方式详解
2019/09/01 Javascript
详解小程序云开发攻略(解决最棘手的问题)
2019/09/30 Javascript
JS桶排序的简单理解与实现方法示例
2019/11/25 Javascript
vue列表数据发生变化指令没有更新问题及解决方法
2020/01/16 Javascript
node+vue实现文件上传功能
2020/05/28 Javascript
[01:51]开启你的城市传奇 完美世界城市挑战赛开始报名
2018/10/09 DOTA
Python实现的使用telnet登陆聊天室实例
2015/06/17 Python
python实现按任意键继续执行程序
2016/12/30 Python
Python实现Dijkstra算法
2018/10/17 Python
Python 编程速成(推荐)
2019/04/15 Python
对python中的*args与**kwgs的含义与作用详解
2019/08/28 Python
详解python uiautomator2 watcher的使用方法
2019/09/09 Python
django-xadmin根据当前登录用户动态设置表单字段默认值方式
2020/03/13 Python
python对XML文件的操作实现代码
2020/03/27 Python
3分钟看懂Python后端必须知道的Django的信号机制
2020/07/26 Python
发现世界上最好的珠宝设计师:JewelStreet
2017/12/17 全球购物
JSF面试题:如何管量web层中的Bean,用什么标签。如何通过jsp页面与Bean绑定在一起进行处理?
2012/10/05 面试题
化工专业大学生职业生涯规划书
2014/01/14 职场文书
会计专业大学生职业生涯规划书
2014/02/11 职场文书
四风之害观后感
2015/06/09 职场文书
Python办公自动化之Excel(中)
2021/05/24 Python