基于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和原生JavaScript实现网页定位导航特效
Apr 03 jQuery
jquery.form.js异步提交表单详解
Apr 25 jQuery
jquery实现一个全局计时器(商城可用)
Jun 30 jQuery
jQuery动态添加.active 实现导航效果代码思路详解
Aug 29 jQuery
jquery ajaxfileupload异步上传插件
Nov 21 jQuery
浅析vue-router jquery和params传参(接收参数)$router $route的区别
Aug 03 jQuery
jQuery时间戳和日期相互转换操作示例
Dec 07 jQuery
jQuery实现为table表格动态添加或删除tr功能示例
Feb 19 jQuery
Vue CLI3.0中使用jQuery和Bootstrap的方法
Feb 28 jQuery
jquery使用echarts实现有向图可视化功能示例
Nov 25 jQuery
jQuery实现开关灯效果
Aug 02 jQuery
jQuery编写QQ简易聊天框
Aug 27 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/09/17 PHP
MySql数据库查询结果用表格输出PHP代码示例
2015/03/20 PHP
PHP中异常处理的一些方法整理
2015/07/03 PHP
PHP 设计模式系列之 specification规格模式
2016/01/10 PHP
ThinkPHP中类的构造函数_construct()与_initialize()的区别详解
2017/03/13 PHP
php7安装mongoDB扩展的方法分析
2017/08/02 PHP
jQuery之排序组件的深入解析
2013/06/19 Javascript
原生Js实现元素渐隐/渐现(原理为修改元素的css透明度)
2013/06/24 Javascript
node.js中的fs.utimesSync方法使用说明
2014/12/15 Javascript
JS点击链接后慢慢展开隐藏着图片的方法
2015/02/17 Javascript
JavaScript中String.prototype用法实例
2015/05/20 Javascript
深入浅析Node.js 事件循环
2015/12/20 Javascript
微信js-sdk上传与下载图片接口用法示例
2016/10/12 Javascript
jQuery用FormData实现文件上传的方法
2016/11/21 Javascript
Bootstrap 模态框(Modal)带参数传值实例
2017/08/20 Javascript
vue使用 better-scroll的参数和方法详解
2018/01/25 Javascript
基于nodejs res.end和res.send的区别
2018/05/14 NodeJs
vue实现文件上传功能
2018/08/13 Javascript
详解关于Vue2.0路由开启keep-alive时需要注意的地方
2018/09/18 Javascript
ionic3双击返回退出应用的方法
2019/09/17 Javascript
利用webpack理解CommonJS和ES Modules的差异区别
2020/06/16 Javascript
Ant Design的可编辑Tree的实现操作
2020/10/31 Javascript
vant 中van-list的用法说明
2020/11/11 Javascript
[40:57]TI4 循环赛第二日 iG vs EG
2014/07/11 DOTA
python通过正则查找微博@(at)用户的方法
2015/03/13 Python
Python正则表达式非贪婪、多行匹配功能示例
2017/08/08 Python
python实时监控cpu小工具
2018/06/21 Python
Django页面数据的缓存与使用的具体方法
2019/04/23 Python
Python-openCV读RGB通道图实例
2020/01/17 Python
安装python3.7编译器后如何正确安装opnecv的方法详解
2020/06/16 Python
银河香水:Galaxy Perfume
2019/03/25 全球购物
党的群众路线教育实践活动调研报告
2014/11/03 职场文书
商超业务员岗位职责
2015/02/13 职场文书
2015年教师节演讲稿范文
2015/03/19 职场文书
分享mysql的current_timestamp小坑及解决
2021/11/27 MySQL
Apache SkyWalking 监控 MySQL Server 实战解析
2022/09/23 Servers