需求描述

需要实现类似QQ中对联系人的操作:向左滑动,滑出删除按钮。滑动超过一半时松开则自动滑到底,不到一半时松开则返回原处。

纯js实现

使用了h5的touchmove等事件,以及用js动态改变css3的translate属性来达到动画效果:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3.  
  4. <head>
  5. <meta charset="UTF-8">
  6. <meta name="viewport" id="viewport" content="width=device-width, initial-scale=1">
  7. <title>html5向左滑动删除特效</title>
  8. <style>
  9. * {
  10. padding: 0;
  11. margin: 0;
  12. list-style: none;
  13. }
  14.  
  15. header {
  16. background: #f7483b;
  17. border-bottom: 1px solid #ccc
  18. }
  19.  
  20. header h2 {
  21. text-align: center;
  22. line-height: 54px;
  23. font-size: 16px;
  24. color: #fff
  25. }
  26.  
  27. .list-ul {
  28. overflow: hidden
  29. }
  30.  
  31. .list-li {
  32. line-height: 60px;
  33. border-bottom: 1px solid #fcfcfc;
  34. position: relative;
  35. padding: 0 12px;
  36. color: #666;
  37. background: #f2f2f2;
  38. -webkit-transform: translateX(0px);
  39. }
  40.  
  41. .btn {
  42. position: absolute;
  43. top: 0;
  44. right: -80px;
  45. text-align: center;
  46. background: #ffcb20;
  47. color: #fff;
  48. width: 80px
  49. }
  50. </style>
  51. <script>
  52. /*
  53. * 描述:html5苹果手机向左滑动删除特效
  54. */
  55. window.addEventListener('load', function() {
  56. var initX; //触摸位置
  57. var moveX; //滑动时的位置
  58. var X = 0; //移动距离
  59. var objX = 0; //目标对象位置
  60. window.addEventListener('touchstart', function(event) {
  61. event.preventDefault();
  62. var obj = event.target.parentNode;
  63. if (obj.className == "list-li") {
  64. initX = event.targetTouches[0].pageX;
  65. objX = (obj.style.WebkitTransform.replace(/translateX\(/g, "").replace(/px\)/g, "")) * 1;
  66. }
  67. if (objX == 0) {
  68. window.addEventListener('touchmove', function(event) {
  69. event.preventDefault();
  70. var obj = event.target.parentNode;
  71. if (obj.className == "list-li") {
  72. moveX = event.targetTouches[0].pageX;
  73. X = moveX - initX;
  74. if (X >= 0) {
  75. obj.style.WebkitTransform = "translateX(" + 0 + "px)";
  76. } else if (X < 0) {
  77. var l = Math.abs(X);
  78. obj.style.WebkitTransform = "translateX(" + -l + "px)";
  79. if (l > 80) {
  80. l = 80;
  81. obj.style.WebkitTransform = "translateX(" + -l + "px)";
  82. }
  83. }
  84. }
  85. });
  86. } else if (objX < 0) {
  87. window.addEventListener('touchmove', function(event) {
  88. event.preventDefault();
  89. var obj = event.target.parentNode;
  90. if (obj.className == "list-li") {
  91. moveX = event.targetTouches[0].pageX;
  92. X = moveX - initX;
  93. if (X >= 0) {
  94. var r = -80 + Math.abs(X);
  95. obj.style.WebkitTransform = "translateX(" + r + "px)";
  96. if (r > 0) {
  97. r = 0;
  98. obj.style.WebkitTransform = "translateX(" + r + "px)";
  99. }
  100. } else { //向左滑动
  101. obj.style.WebkitTransform = "translateX(" + -80 + "px)";
  102. }
  103. }
  104. });
  105. }
  106.  
  107. })
  108. window.addEventListener('touchend', function(event) {
  109. event.preventDefault();
  110. var obj = event.target.parentNode;
  111. if (obj.className == "list-li") {
  112. objX = (obj.style.WebkitTransform.replace(/translateX\(/g, "").replace(/px\)/g, "")) * 1;
  113. if (objX > -40) {
  114. obj.style.WebkitTransform = "translateX(" + 0 + "px)";
  115. objX = 0;
  116. } else {
  117. obj.style.WebkitTransform = "translateX(" + -80 + "px)";
  118. objX = -80;
  119. }
  120. }
  121. })
  122. })
  123. </script>
  124. </head>
  125.  
  126. <body>
  127. <header>
  128. <h2>消息列表</h2>
  129. </header>
  130. <section class="list">
  131. <ul class="list-ul">
  132. <li id="li" class="list-li">
  133. <div class="con">
  134. 你的快递到了,请到楼下签收
  135. </div>
  136. <div class="btn">删除</div>
  137. </li>
  138. <li class="list-li">
  139. <div class="con">
  140. 哇,你在干嘛,快点来啊就等你了
  141. </div>
  142. <div class="btn">删除</div>
  143. </li>
  144. </ul>
  145. </section>
  146. </body>
  147.  
  148. </html>

做成zepto插件

实际项目中,我们可能有很多个地方会用到这个功能。现在我们将这个功能做成zepto插件,方便后面使用。

这个插件,我们仅实现这个功能,然后传入参数(删除按钮的样式名),让程序在js中计算所需要滑动的距离,方便复用。

zepto.touchWipe.js

  1. /**
  2. * zepto插件:向左滑动删除动效
  3. * 使用方法:$('.itemWipe').touchWipe({itemDelete: '.item-delete'});
  4. * 参数:itemDelete 删除按钮的样式名
  5. */
  6. ;
  7. (function($) {
  8. $.fn.touchWipe = function(option) {
  9. var defaults = {
  10. itemDelete: '.item-delete', //删除元素
  11. };
  12. var opts = $.extend({}, defaults, option); //配置选项
  13.  
  14. var delWidth = $(opts.itemDelete).width();
  15.  
  16. var initX; //触摸位置
  17. var moveX; //滑动时的位置
  18. var X = 0; //移动距离
  19. var objX = 0; //目标对象位置
  20. $(this).on('touchstart', function(event) {
  21. event.preventDefault();
  22. var obj = this;
  23. initX = event.targetTouches[0].pageX;
  24. objX = (obj.style.WebkitTransform.replace(/translateX\(/g, "").replace(/px\)/g, "")) * 1;
  25. if (objX == 0) {
  26. $(this).on('touchmove', function(event) {
  27. event.preventDefault();
  28. var obj = this;
  29. moveX = event.targetTouches[0].pageX;
  30. X = moveX - initX;
  31. if (X >= 0) {
  32. obj.style.WebkitTransform = "translateX(" + 0 + "px)";
  33. } else if (X < 0) {
  34. var l = Math.abs(X);
  35. obj.style.WebkitTransform = "translateX(" + -l + "px)";
  36. if (l > delWidth) {
  37. l = delWidth;
  38. obj.style.WebkitTransform = "translateX(" + -l + "px)";
  39. }
  40. }
  41. });
  42. } else if (objX < 0) {
  43. $(this).on('touchmove', function(event) {
  44. event.preventDefault();
  45. var obj = this;
  46. moveX = event.targetTouches[0].pageX;
  47. X = moveX - initX;
  48. if (X >= 0) {
  49. var r = -delWidth + Math.abs(X);
  50. obj.style.WebkitTransform = "translateX(" + r + "px)";
  51. if (r > 0) {
  52. r = 0;
  53. obj.style.WebkitTransform = "translateX(" + r + "px)";
  54. }
  55. } else { //向左滑动
  56. obj.style.WebkitTransform = "translateX(" + -delWidth + "px)";
  57. }
  58. });
  59. }
  60.  
  61. })
  62. $(this).on('touchend', function(event) {
  63. event.preventDefault();
  64. var obj = this;
  65. objX = (obj.style.WebkitTransform.replace(/translateX\(/g, "").replace(/px\)/g, "")) * 1;
  66. if (objX > -delWidth / 2) {
  67. obj.style.transition = "all 0.2s";
  68. obj.style.WebkitTransform = "translateX(" + 0 + "px)";
  69. obj.style.transition = "all 0";
  70. objX = 0;
  71. } else {
  72. obj.style.transition = "all 0.2s";
  73. obj.style.WebkitTransform = "translateX(" + -delWidth + "px)";
  74. obj.style.transition = "all 0";
  75. objX = -delWidth;
  76. }
  77. })
  78.  
  79. //链式返回
  80. return this;
  81. };
  82.  
  83. })(Zepto);

touchWipe.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" id="viewport" content="width=device-width, initial-scale=1">
  6. <title>html5向左滑动删除特效</title>
  7.  
  8. <style>
  9. *{ padding:0; margin:0; list-style: none;}
  10. header{ background: #f7483b; border-bottom: 1px solid #ccc}
  11. header h2{ text-align: center; line-height: 54px; font-size: 16px; color: #fff}
  12. .list-ul{ overflow: hidden}
  13. .list-li{ line-height: 60px; border-bottom: 1px solid #fcfcfc; position:relative;padding: 0 12px; color: #666;
  14. background: #f2f2f2;
  15. -webkit-transform: translateX(0px);
  16. }
  17. .btn{ position: absolute; top: 0; right: -80px; text-align: center; background: #ffcb20; color: #fff; width: 80px}
  18. </style>
  19.  
  20. </head>
  21. <body>
  22. <header>
  23. <h2>消息列表</h2>
  24. </header>
  25. <section class="list">
  26. <ul class="list-ul">
  27. <li id="li" class="list-li">
  28. <div class="con">
  29. 你的快递到了,请到楼下签收
  30. </div>
  31. <div class="btn">删除</div>
  32. </li>
  33. <li class="list-li">
  34. <div class="con">
  35. 哇,你在干嘛,快点来啊就等你了
  36. </div>
  37. <div class="btn">删除</div>
  38. </li>
  39. </ul>
  40. </section>
  41.  
  42. <p>X: <span id="X"></span></p>
  43. <p>objX: <span id="objX"></span></p>
  44. <p>initX: <span id="initX"></span></p>
  45. <p>moveX: <span id="moveX"></span></p>
  46.  
  47. <script type="text/javascript" src="http://apps.bdimg.com/libs/zepto/1.1.4/zepto.min.js"></script>
  48. <script type="text/javascript" src="zepto.touchWipe.js"></script>
  49. <script type="text/javascript">
  50. $(function() {
  51. $('.list-li').touchWipe({itemDelete: '.btn'});
  52. });
  53.  
  54. </script>
  55. </body>
  56. </html>

效果:

实际项目中的应用效果:

消除BUG

到上面一步,基本实现了我们所需要的功能。但是有几个问题

1. 右边的删除按钮点击失灵,因为span无法冒泡到大按钮上;

2. 非常严重的问题,我们给div添加了touchmove事件同时用preventDefault()屏蔽了原始的浏览器事件,导致上下滑动div的时候 页面无法滚动了!

第一个问题比较容易解决,我们把span直接去掉,将“删除”写到css中的:before里,像这样:

  1. .itemWipe .item-delete:before {
  2. content: '删除';
  3. color: #fff;
  4. }

对于第二个问题,网上说用iscroll来解决。我们这里参考手机QQ中对联系人的滑动操作。

大致原理:在滑动最开始的时候,判断是Y轴的移动多 还是 X轴的移动多。 如果是X轴移动大,则判断为滑动删除操作,我们再使用preventDefault();

其中,我们加入一个变量flaxX来判断滑动方向,会在第一次触发滑动事件时设置。

之后就可以根据滑动方向来选择是否阻止浏览器默认事件了。

修改后: zepto.touchWipe.js

  1. /**
  2. * zepto插件:向左滑动删除动效
  3. * 使用方法:$('.itemWipe').touchWipe({itemDelete: '.item-delete'});
  4. * 参数:itemDelete 删除按钮的样式名
  5. */
  6. ;
  7. (function($) {
  8. $.fn.touchWipe = function(option) {
  9. var defaults = {
  10. itemDelete: '.item-delete', //删除元素
  11. };
  12. var opts = $.extend({}, defaults, option); //配置选项
  13. var delWidth = $(opts.itemDelete).width();
  14.  
  15. var initX; //触摸位置X
  16. var initY; //触摸位置Y
  17. var moveX; //滑动时的位置X
  18. var moveY; //滑动时的位置Y
  19. var X = 0; //移动距离X
  20. var Y = 0; //移动距离Y
  21. var flagX = 0; //是否是左右滑动 0为初始,1为左右,2为上下,在move中设置,在end中归零
  22. var objX = 0; //目标对象位置
  23.  
  24. $(this).on('touchstart', function(event) {
  25. console.log('start..');
  26. var obj = this;
  27. initX = event.targetTouches[0].pageX;
  28. initY = event.targetTouches[0].pageY;
  29. console.log(initX + ':' + initY);
  30. objX = (obj.style.WebkitTransform.replace(/translateX\(/g, "").replace(/px\)/g, "")) * 1;
  31. console.log (objX);
  32. if (objX == 0) {
  33. $(this).on('touchmove', function(event) {
  34.  
  35. // 判断滑动方向,X轴阻止默认事件,Y轴跳出使用浏览器默认
  36. if (flagX == 0) {
  37. setScrollX(event);
  38. return;
  39. } else if (flagX == 1) {
  40. event.preventDefault();
  41. } else {
  42. return;
  43. }
  44.  
  45. var obj = this;
  46. moveX = event.targetTouches[0].pageX;
  47. X = moveX - initX;
  48. if (X >= 0) {
  49. obj.style.WebkitTransform = "translateX(" + 0 + "px)";
  50. } else if (X < 0) {
  51. var l = Math.abs(X);
  52. obj.style.WebkitTransform = "translateX(" + -l + "px)";
  53. if (l > delWidth) {
  54. l = delWidth;
  55. obj.style.WebkitTransform = "translateX(" + -l + "px)";
  56. }
  57. }
  58. });
  59. } else if (objX < 0) {
  60. $(this).on('touchmove', function(event) {
  61.  
  62. // 判断滑动方向,X轴阻止默认事件,Y轴跳出使用浏览器默认
  63. if (flagX == 0) {
  64. setScrollX(event);
  65. return;
  66. } else if (flagX == 1) {
  67. event.preventDefault();
  68. } else {
  69. return;
  70. }
  71.  
  72. var obj = this;
  73. moveX = event.targetTouches[0].pageX;
  74. X = moveX - initX;
  75. if (X >= 0) {
  76. var r = -delWidth + Math.abs(X);
  77. obj.style.WebkitTransform = "translateX(" + r + "px)";
  78. if (r > 0) {
  79. r = 0;
  80. obj.style.WebkitTransform = "translateX(" + r + "px)";
  81. }
  82. } else { //向左滑动
  83. obj.style.WebkitTransform = "translateX(" + -delWidth + "px)";
  84. }
  85. });
  86. }
  87. })
  88.  
  89. //结束时判断,并自动滑动到底或返回
  90. $(this).on('touchend', function(event) {
  91. var obj = this;
  92. objX = (obj.style.WebkitTransform.replace(/translateX\(/g, "").replace(/px\)/g, "")) * 1;
  93. if (objX > -delWidth / 2) {
  94. obj.style.transition = "all 0.2s";
  95. obj.style.WebkitTransform = "translateX(" + 0 + "px)";
  96. obj.style.transition = "all 0";
  97. objX = 0;
  98. } else {
  99. obj.style.transition = "all 0.2s";
  100. obj.style.WebkitTransform = "translateX(" + -delWidth + "px)";
  101. obj.style.transition = "all 0";
  102. objX = -delWidth;
  103. }
  104. flagX = 0;
  105. })
  106.  
  107. //设置滑动方向
  108. function setScrollX(event) {
  109. moveX = event.targetTouches[0].pageX;
  110. moveY = event.targetTouches[0].pageY;
  111. X = moveX - initX;
  112. Y = moveY - initY;
  113.  
  114. if (Math.abs(X) > Math.abs(Y)) {
  115. flagX = 1;
  116. } else {
  117. flagX = 2;
  118. }
  119. return flagX;
  120. }
  121.  
  122. //链式返回
  123. return this;
  124. };
  125.  
  126. })(Zepto);

写一个js向左滑动删除 交互特效的插件——Html5 touchmove的更多相关文章

  1. android QQ消息左滑动删除实例(优化版SwipeListViewEX)

    仿 QQ消息左滑动删除item消息实例 源代码参考:http://blog.csdn.net/gaolei1201/article/details/42677951 自己作了一些调整,全部代码下载地址 ...

  2. html5向左滑动删除特效

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. iOS边练边学--简单的数据操作(增、删、改),左滑动删除和弹窗

    一.数据刷新的原则: 通过修改模型数据,来修改tableView的展示 先修改数据模型 在调用数据刷新方法 不要直接修改cell上面子控件的属性 二.增删改用到的方法: <1>重新绑定屏幕 ...

  4. 让我们纯手写一个js继承吧

    继承在前端逻辑操作中是比较常见的,今天我们就从零开始写一个js的继承方式 在es5中继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上Parent.call(this),在es6中则 ...

  5. 如何写一个Js上传图片插件。

    项目里面需要一个上传图片的插件,找了半天没有找到满意的,算了 不找了,自己写一个吧,顺便复习一下js方面的知识.完成之后效果还不错,当然还要继续优化,源码在最后. 介绍一种常见的js插件的写法 ; ( ...

  6. 前端与编译原理——用JS写一个JS解释器

    说起编译原理,印象往往只停留在本科时那些枯燥的课程和晦涩的概念.作为前端开发者,编译原理似乎离我们很远,对它的理解很可能仅仅局限于"抽象语法树(AST)".但这仅仅是个开头而已.编 ...

  7. 从 0 到 1 到完美,写一个 js 库、node 库、前端组件库

    之前讲了很多关于项目工程化.前端架构.前端构建等方面的技术,这次说说怎么写一个完美的第三方库. 1. 选择合适的规范来写代码 js 模块化的发展大致有这样一个过程 iife => commonj ...

  8. 如何手写一个js工具库?同时发布到npm上

    自从工作以来,写项目的时候经常需要手写一些方法和引入一些js库 JS基础又十分重要,于是就萌生出自己创建一个JS工具库并发布到npm上的想法 于是就创建了一个名为learnjts的项目,在空余时间也写 ...

  9. 写一个限制上传文件大小和格式的jQuery插件

    在客户端上传文件,通常需要限制文件的尺寸和格式,最常用的做法是使用某款插件,一些成熟的插件的确界面好看,且功能强大,但美中不足的是:有时候会碰到浏览器兼容问题.本篇就来写一个"原生态&quo ...

随机推荐

  1. REDIS持久化报错失败

    redis log报错: [7666] 15 Jan 00:22:36.028 # Error moving temp DB file on the final destination: Invali ...

  2. 浅析jquery ajax异步调用方法中不能给全局变量赋值的原因及解决方法(转载)

    在调用一个jquery的ajax方法时我们有时会需要该方法返回一个值或者给某个全局变量赋值,可是我们发现程序执行完后并没有获取到我们想要的值,这时很有可能是因为你用的是ajax的异步调用async:t ...

  3. Java的修饰符

    转自:http://blog.csdn.net/manyizilin/article/details/51926230#L42 修饰符: 像其他语言一样,Java可以使用修饰符来修饰类中方法和属性.主 ...

  4. TCP/IP协议详解——邮差与邮局

    信号的传输总要符合一定的协议.比如说长城上放狼烟,是因为人们已经预先设定好狼烟这个物理信号代表了“敌人入侵”这一抽象信号.这样一个“狼烟=敌人入侵”就是一个简单的协议. 信号的传输总要符合一定的协议( ...

  5. OBS MAC 系统开发(基于mac OS X 10.12)

    按照github 上的说明,安装配套软件,和跟踪需要的库 推荐使用homebrew 来安装各种依赖库. 安装Qt后,要配置系统变量 ,这个困扰本人很久:) 成功编译 cmake .. &&am ...

  6. supervisor的使用:

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Helvetica Neue"; color: #e4af0a } sp ...

  7. C内嵌汇编-格式

    C内嵌汇编-格式: __asm__(汇编语句部分:输出部分:输入部分破坏描述部分);C内嵌汇编以关键字"__asm__"或"asm"开始, 下辖四个部分, 各部 ...

  8. c# 动态调用WCF方法笔记!

    //动态调用wcf方法 string url = "http://localhost:54379/ServiceWCF.svc"; IDoubleService proxy = W ...

  9. selenium 定位元素

    一.单个元素的定位方式: By.className(className))By.cssSelector(selector)By.id(id)By.linkText(linkText)By.name(n ...

  10. Android中仿IOS提示框的实现

    前言 在Android开发中,我们有时需要实现类似IOS的对话框.今天我就来总结下,如何通过自定义的开发来实现类似的功能. 自定义Dialog 我们知道Android中最常用的对话框就是Dialog及 ...