基于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,Angular实现登录界面验证码详解
Apr 27 jQuery
jQuery选择器之表单元素选择器详解
Sep 19 jQuery
认识jQuery的Promise的具体使用方法
Oct 10 jQuery
使用JQuery实现图片轮播效果的实例(推荐)
Oct 24 jQuery
jQuery实现的下雪动画效果示例【附源码下载】
Feb 02 jQuery
jQuery实现checkbox全选功能完整实例
Jul 12 jQuery
解决jquery的ajax调取后端数据成功却渲染失败的问题
Aug 08 jQuery
jQuery实现鼠标移入移出事件切换功能示例
Sep 06 jQuery
jquery登录的异步验证操作示例
May 09 jQuery
jQuery 淡入/淡出效果函数用法分析
May 19 jQuery
jquery实现抽奖功能
Oct 22 jQuery
jQuery实现影院选座订座效果
Apr 13 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
DC最新动画电影:《战争之子》为何内容偏激,毁了一个不错的漫画
2020/04/09 欧美动漫
php过滤危险html代码
2008/08/18 PHP
php编程实现简单的网页版计算器功能示例
2017/04/26 PHP
js DataSet数据源处理代码
2010/03/29 Javascript
js实现右下角可关闭最小化div(可用于展示推荐内容)
2013/06/24 Javascript
jQuery制作仿腾讯web qq用户体验桌面
2013/08/20 Javascript
php读取sqlite数据库入门实例代码
2014/06/25 Javascript
jQuery简单实现隐藏以及显示特效
2015/02/26 Javascript
浅谈javascript的分号的使用
2015/05/12 Javascript
JS文字球状放大效果代码分享
2015/08/19 Javascript
Vue-Router实现页面正在加载特效方法示例
2017/02/12 Javascript
利用JavaScript对中文(汉字)进行排序实例详解
2017/06/18 Javascript
Vue AST源码解析第一篇
2017/07/19 Javascript
详解jquery插件jquery.viewport.js学习使用方法
2017/09/08 jQuery
JavaScript数组push方法使用注意事项
2017/10/30 Javascript
详解Vue+Element的动态表单,动态表格(后端发送配置,前端动态生成)
2019/04/20 Javascript
VUE+elementui面包屑实现动态路由详解
2019/11/04 Javascript
vue实现商城秒杀倒计时功能
2019/12/12 Javascript
JavaScript中CreateTextFile函数
2020/08/30 Javascript
详解Vue2的diff算法
2021/01/06 Vue.js
[43:33]EG vs Spirit Supermajor 败者组 BO3 第一场 6.4
2018/06/05 DOTA
Python中的闭包详细介绍和实例
2014/11/21 Python
Python中的日期时间处理详解
2016/11/17 Python
Python输出各行命令详解
2018/02/01 Python
Python小游戏之300行代码实现俄罗斯方块
2019/01/04 Python
浅析PyTorch中nn.Module的使用
2019/08/18 Python
Python 实现文件读写、坐标寻址、查找替换功能
2019/09/11 Python
使用Python来做一个屏幕录制工具的操作代码
2020/01/18 Python
Python matplotlib画曲线例题解析
2020/02/07 Python
弄清Pytorch显存的分配机制
2020/12/10 Python
高级销售员求职信
2013/10/25 职场文书
2014年中班元旦活动方案
2014/02/14 职场文书
2014新年元旦活动策划方案
2014/02/18 职场文书
2014法制宣传日活动总结
2014/07/09 职场文书
导游词之台湾阿里山
2019/10/23 职场文书
Css预编语言及区别详解
2021/04/25 HTML / CSS