写一个js向左滑动删除 交互特效的插件——Html5 touchmove
需求描述
需要实现类似QQ中对联系人的操作:向左滑动,滑出删除按钮。滑动超过一半时松开则自动滑到底,不到一半时松开则返回原处。
纯js实现
使用了h5的touchmove等事件,以及用js动态改变css3的translate属性来达到动画效果:
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" id="viewport" content="width=device-width, initial-scale=1">
- <title>html5向左滑动删除特效</title>
- <style>
- * {
- padding: 0;
- margin: 0;
- list-style: none;
- }
- header {
- background: #f7483b;
- border-bottom: 1px solid #ccc
- }
- header h2 {
- text-align: center;
- line-height: 54px;
- font-size: 16px;
- color: #fff
- }
- .list-ul {
- overflow: hidden
- }
- .list-li {
- line-height: 60px;
- border-bottom: 1px solid #fcfcfc;
- position: relative;
- padding: 0 12px;
- color: #666;
- background: #f2f2f2;
- -webkit-transform: translateX(0px);
- }
- .btn {
- position: absolute;
- top: 0;
- right: -80px;
- text-align: center;
- background: #ffcb20;
- color: #fff;
- width: 80px
- }
- </style>
- <script>
- /*
- * 描述:html5苹果手机向左滑动删除特效
- */
- window.addEventListener('load', function() {
- var initX; //触摸位置
- var moveX; //滑动时的位置
- var X = 0; //移动距离
- var objX = 0; //目标对象位置
- window.addEventListener('touchstart', function(event) {
- event.preventDefault();
- var obj = event.target.parentNode;
- if (obj.className == "list-li") {
- initX = event.targetTouches[0].pageX;
- objX = (obj.style.WebkitTransform.replace(/translateX\(/g, "").replace(/px\)/g, "")) * 1;
- }
- if (objX == 0) {
- window.addEventListener('touchmove', function(event) {
- event.preventDefault();
- var obj = event.target.parentNode;
- if (obj.className == "list-li") {
- moveX = event.targetTouches[0].pageX;
- X = moveX - initX;
- if (X >= 0) {
- obj.style.WebkitTransform = "translateX(" + 0 + "px)";
- } else if (X < 0) {
- var l = Math.abs(X);
- obj.style.WebkitTransform = "translateX(" + -l + "px)";
- if (l > 80) {
- l = 80;
- obj.style.WebkitTransform = "translateX(" + -l + "px)";
- }
- }
- }
- });
- } else if (objX < 0) {
- window.addEventListener('touchmove', function(event) {
- event.preventDefault();
- var obj = event.target.parentNode;
- if (obj.className == "list-li") {
- moveX = event.targetTouches[0].pageX;
- X = moveX - initX;
- if (X >= 0) {
- var r = -80 + Math.abs(X);
- obj.style.WebkitTransform = "translateX(" + r + "px)";
- if (r > 0) {
- r = 0;
- obj.style.WebkitTransform = "translateX(" + r + "px)";
- }
- } else { //向左滑动
- obj.style.WebkitTransform = "translateX(" + -80 + "px)";
- }
- }
- });
- }
- })
- window.addEventListener('touchend', function(event) {
- event.preventDefault();
- var obj = event.target.parentNode;
- if (obj.className == "list-li") {
- objX = (obj.style.WebkitTransform.replace(/translateX\(/g, "").replace(/px\)/g, "")) * 1;
- if (objX > -40) {
- obj.style.WebkitTransform = "translateX(" + 0 + "px)";
- objX = 0;
- } else {
- obj.style.WebkitTransform = "translateX(" + -80 + "px)";
- objX = -80;
- }
- }
- })
- })
- </script>
- </head>
- <body>
- <header>
- <h2>消息列表</h2>
- </header>
- <section class="list">
- <ul class="list-ul">
- <li id="li" class="list-li">
- <div class="con">
- 你的快递到了,请到楼下签收
- </div>
- <div class="btn">删除</div>
- </li>
- <li class="list-li">
- <div class="con">
- 哇,你在干嘛,快点来啊就等你了
- </div>
- <div class="btn">删除</div>
- </li>
- </ul>
- </section>
- </body>
- </html>
做成zepto插件
实际项目中,我们可能有很多个地方会用到这个功能。现在我们将这个功能做成zepto插件,方便后面使用。
这个插件,我们仅实现这个功能,然后传入参数(删除按钮的样式名),让程序在js中计算所需要滑动的距离,方便复用。
zepto.touchWipe.js
- /**
- * zepto插件:向左滑动删除动效
- * 使用方法:$('.itemWipe').touchWipe({itemDelete: '.item-delete'});
- * 参数:itemDelete 删除按钮的样式名
- */
- ;
- (function($) {
- $.fn.touchWipe = function(option) {
- var defaults = {
- itemDelete: '.item-delete', //删除元素
- };
- var opts = $.extend({}, defaults, option); //配置选项
- var delWidth = $(opts.itemDelete).width();
- var initX; //触摸位置
- var moveX; //滑动时的位置
- var X = 0; //移动距离
- var objX = 0; //目标对象位置
- $(this).on('touchstart', function(event) {
- event.preventDefault();
- var obj = this;
- initX = event.targetTouches[0].pageX;
- objX = (obj.style.WebkitTransform.replace(/translateX\(/g, "").replace(/px\)/g, "")) * 1;
- if (objX == 0) {
- $(this).on('touchmove', function(event) {
- event.preventDefault();
- var obj = this;
- moveX = event.targetTouches[0].pageX;
- X = moveX - initX;
- if (X >= 0) {
- obj.style.WebkitTransform = "translateX(" + 0 + "px)";
- } else if (X < 0) {
- var l = Math.abs(X);
- obj.style.WebkitTransform = "translateX(" + -l + "px)";
- if (l > delWidth) {
- l = delWidth;
- obj.style.WebkitTransform = "translateX(" + -l + "px)";
- }
- }
- });
- } else if (objX < 0) {
- $(this).on('touchmove', function(event) {
- event.preventDefault();
- var obj = this;
- moveX = event.targetTouches[0].pageX;
- X = moveX - initX;
- if (X >= 0) {
- var r = -delWidth + Math.abs(X);
- obj.style.WebkitTransform = "translateX(" + r + "px)";
- if (r > 0) {
- r = 0;
- obj.style.WebkitTransform = "translateX(" + r + "px)";
- }
- } else { //向左滑动
- obj.style.WebkitTransform = "translateX(" + -delWidth + "px)";
- }
- });
- }
- })
- $(this).on('touchend', function(event) {
- event.preventDefault();
- var obj = this;
- objX = (obj.style.WebkitTransform.replace(/translateX\(/g, "").replace(/px\)/g, "")) * 1;
- if (objX > -delWidth / 2) {
- obj.style.transition = "all 0.2s";
- obj.style.WebkitTransform = "translateX(" + 0 + "px)";
- obj.style.transition = "all 0";
- objX = 0;
- } else {
- obj.style.transition = "all 0.2s";
- obj.style.WebkitTransform = "translateX(" + -delWidth + "px)";
- obj.style.transition = "all 0";
- objX = -delWidth;
- }
- })
- //链式返回
- return this;
- };
- })(Zepto);
touchWipe.html
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" id="viewport" content="width=device-width, initial-scale=1">
- <title>html5向左滑动删除特效</title>
- <style>
- *{ padding:0; margin:0; list-style: none;}
- header{ background: #f7483b; border-bottom: 1px solid #ccc}
- header h2{ text-align: center; line-height: 54px; font-size: 16px; color: #fff}
- .list-ul{ overflow: hidden}
- .list-li{ line-height: 60px; border-bottom: 1px solid #fcfcfc; position:relative;padding: 0 12px; color: #666;
- background: #f2f2f2;
- -webkit-transform: translateX(0px);
- }
- .btn{ position: absolute; top: 0; right: -80px; text-align: center; background: #ffcb20; color: #fff; width: 80px}
- </style>
- </head>
- <body>
- <header>
- <h2>消息列表</h2>
- </header>
- <section class="list">
- <ul class="list-ul">
- <li id="li" class="list-li">
- <div class="con">
- 你的快递到了,请到楼下签收
- </div>
- <div class="btn">删除</div>
- </li>
- <li class="list-li">
- <div class="con">
- 哇,你在干嘛,快点来啊就等你了
- </div>
- <div class="btn">删除</div>
- </li>
- </ul>
- </section>
- <p>X: <span id="X"></span></p>
- <p>objX: <span id="objX"></span></p>
- <p>initX: <span id="initX"></span></p>
- <p>moveX: <span id="moveX"></span></p>
- <script type="text/javascript" src="http://apps.bdimg.com/libs/zepto/1.1.4/zepto.min.js"></script>
- <script type="text/javascript" src="zepto.touchWipe.js"></script>
- <script type="text/javascript">
- $(function() {
- $('.list-li').touchWipe({itemDelete: '.btn'});
- });
- </script>
- </body>
- </html>
效果:
实际项目中的应用效果:
消除BUG
到上面一步,基本实现了我们所需要的功能。但是有几个问题:
1. 右边的删除按钮点击失灵,因为span无法冒泡到大按钮上;
2. 非常严重的问题,我们给div添加了touchmove事件同时用preventDefault()屏蔽了原始的浏览器事件,导致上下滑动div的时候 页面无法滚动了!
第一个问题比较容易解决,我们把span直接去掉,将“删除”写到css中的:before里,像这样:
- .itemWipe .item-delete:before {
- content: '删除';
- color: #fff;
- }
对于第二个问题,网上说用iscroll来解决。我们这里参考手机QQ中对联系人的滑动操作。
大致原理:在滑动最开始的时候,判断是Y轴的移动多 还是 X轴的移动多。 如果是X轴移动大,则判断为滑动删除操作,我们再使用preventDefault();
其中,我们加入一个变量flaxX来判断滑动方向,会在第一次触发滑动事件时设置。
之后就可以根据滑动方向来选择是否阻止浏览器默认事件了。
修改后: zepto.touchWipe.js
- /**
- * zepto插件:向左滑动删除动效
- * 使用方法:$('.itemWipe').touchWipe({itemDelete: '.item-delete'});
- * 参数:itemDelete 删除按钮的样式名
- */
- ;
- (function($) {
- $.fn.touchWipe = function(option) {
- var defaults = {
- itemDelete: '.item-delete', //删除元素
- };
- var opts = $.extend({}, defaults, option); //配置选项
- var delWidth = $(opts.itemDelete).width();
- var initX; //触摸位置X
- var initY; //触摸位置Y
- var moveX; //滑动时的位置X
- var moveY; //滑动时的位置Y
- var X = 0; //移动距离X
- var Y = 0; //移动距离Y
- var flagX = 0; //是否是左右滑动 0为初始,1为左右,2为上下,在move中设置,在end中归零
- var objX = 0; //目标对象位置
- $(this).on('touchstart', function(event) {
- console.log('start..');
- var obj = this;
- initX = event.targetTouches[0].pageX;
- initY = event.targetTouches[0].pageY;
- console.log(initX + ':' + initY);
- objX = (obj.style.WebkitTransform.replace(/translateX\(/g, "").replace(/px\)/g, "")) * 1;
- console.log (objX);
- if (objX == 0) {
- $(this).on('touchmove', function(event) {
- // 判断滑动方向,X轴阻止默认事件,Y轴跳出使用浏览器默认
- if (flagX == 0) {
- setScrollX(event);
- return;
- } else if (flagX == 1) {
- event.preventDefault();
- } else {
- return;
- }
- var obj = this;
- moveX = event.targetTouches[0].pageX;
- X = moveX - initX;
- if (X >= 0) {
- obj.style.WebkitTransform = "translateX(" + 0 + "px)";
- } else if (X < 0) {
- var l = Math.abs(X);
- obj.style.WebkitTransform = "translateX(" + -l + "px)";
- if (l > delWidth) {
- l = delWidth;
- obj.style.WebkitTransform = "translateX(" + -l + "px)";
- }
- }
- });
- } else if (objX < 0) {
- $(this).on('touchmove', function(event) {
- // 判断滑动方向,X轴阻止默认事件,Y轴跳出使用浏览器默认
- if (flagX == 0) {
- setScrollX(event);
- return;
- } else if (flagX == 1) {
- event.preventDefault();
- } else {
- return;
- }
- var obj = this;
- moveX = event.targetTouches[0].pageX;
- X = moveX - initX;
- if (X >= 0) {
- var r = -delWidth + Math.abs(X);
- obj.style.WebkitTransform = "translateX(" + r + "px)";
- if (r > 0) {
- r = 0;
- obj.style.WebkitTransform = "translateX(" + r + "px)";
- }
- } else { //向左滑动
- obj.style.WebkitTransform = "translateX(" + -delWidth + "px)";
- }
- });
- }
- })
- //结束时判断,并自动滑动到底或返回
- $(this).on('touchend', function(event) {
- var obj = this;
- objX = (obj.style.WebkitTransform.replace(/translateX\(/g, "").replace(/px\)/g, "")) * 1;
- if (objX > -delWidth / 2) {
- obj.style.transition = "all 0.2s";
- obj.style.WebkitTransform = "translateX(" + 0 + "px)";
- obj.style.transition = "all 0";
- objX = 0;
- } else {
- obj.style.transition = "all 0.2s";
- obj.style.WebkitTransform = "translateX(" + -delWidth + "px)";
- obj.style.transition = "all 0";
- objX = -delWidth;
- }
- flagX = 0;
- })
- //设置滑动方向
- function setScrollX(event) {
- moveX = event.targetTouches[0].pageX;
- moveY = event.targetTouches[0].pageY;
- X = moveX - initX;
- Y = moveY - initY;
- if (Math.abs(X) > Math.abs(Y)) {
- flagX = 1;
- } else {
- flagX = 2;
- }
- return flagX;
- }
- //链式返回
- return this;
- };
- })(Zepto);
写一个js向左滑动删除 交互特效的插件——Html5 touchmove的更多相关文章
- android QQ消息左滑动删除实例(优化版SwipeListViewEX)
仿 QQ消息左滑动删除item消息实例 源代码参考:http://blog.csdn.net/gaolei1201/article/details/42677951 自己作了一些调整,全部代码下载地址 ...
- html5向左滑动删除特效
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- iOS边练边学--简单的数据操作(增、删、改),左滑动删除和弹窗
一.数据刷新的原则: 通过修改模型数据,来修改tableView的展示 先修改数据模型 在调用数据刷新方法 不要直接修改cell上面子控件的属性 二.增删改用到的方法: <1>重新绑定屏幕 ...
- 让我们纯手写一个js继承吧
继承在前端逻辑操作中是比较常见的,今天我们就从零开始写一个js的继承方式 在es5中继承实质上是先创建子类的实例对象,然后再将父类的方法添加到this上Parent.call(this),在es6中则 ...
- 如何写一个Js上传图片插件。
项目里面需要一个上传图片的插件,找了半天没有找到满意的,算了 不找了,自己写一个吧,顺便复习一下js方面的知识.完成之后效果还不错,当然还要继续优化,源码在最后. 介绍一种常见的js插件的写法 ; ( ...
- 前端与编译原理——用JS写一个JS解释器
说起编译原理,印象往往只停留在本科时那些枯燥的课程和晦涩的概念.作为前端开发者,编译原理似乎离我们很远,对它的理解很可能仅仅局限于"抽象语法树(AST)".但这仅仅是个开头而已.编 ...
- 从 0 到 1 到完美,写一个 js 库、node 库、前端组件库
之前讲了很多关于项目工程化.前端架构.前端构建等方面的技术,这次说说怎么写一个完美的第三方库. 1. 选择合适的规范来写代码 js 模块化的发展大致有这样一个过程 iife => commonj ...
- 如何手写一个js工具库?同时发布到npm上
自从工作以来,写项目的时候经常需要手写一些方法和引入一些js库 JS基础又十分重要,于是就萌生出自己创建一个JS工具库并发布到npm上的想法 于是就创建了一个名为learnjts的项目,在空余时间也写 ...
- 写一个限制上传文件大小和格式的jQuery插件
在客户端上传文件,通常需要限制文件的尺寸和格式,最常用的做法是使用某款插件,一些成熟的插件的确界面好看,且功能强大,但美中不足的是:有时候会碰到浏览器兼容问题.本篇就来写一个"原生态&quo ...
随机推荐
- REDIS持久化报错失败
redis log报错: [7666] 15 Jan 00:22:36.028 # Error moving temp DB file on the final destination: Invali ...
- 浅析jquery ajax异步调用方法中不能给全局变量赋值的原因及解决方法(转载)
在调用一个jquery的ajax方法时我们有时会需要该方法返回一个值或者给某个全局变量赋值,可是我们发现程序执行完后并没有获取到我们想要的值,这时很有可能是因为你用的是ajax的异步调用async:t ...
- Java的修饰符
转自:http://blog.csdn.net/manyizilin/article/details/51926230#L42 修饰符: 像其他语言一样,Java可以使用修饰符来修饰类中方法和属性.主 ...
- TCP/IP协议详解——邮差与邮局
信号的传输总要符合一定的协议.比如说长城上放狼烟,是因为人们已经预先设定好狼烟这个物理信号代表了“敌人入侵”这一抽象信号.这样一个“狼烟=敌人入侵”就是一个简单的协议. 信号的传输总要符合一定的协议( ...
- OBS MAC 系统开发(基于mac OS X 10.12)
按照github 上的说明,安装配套软件,和跟踪需要的库 推荐使用homebrew 来安装各种依赖库. 安装Qt后,要配置系统变量 ,这个困扰本人很久:) 成功编译 cmake .. &&am ...
- supervisor的使用:
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px "Helvetica Neue"; color: #e4af0a } sp ...
- C内嵌汇编-格式
C内嵌汇编-格式: __asm__(汇编语句部分:输出部分:输入部分破坏描述部分);C内嵌汇编以关键字"__asm__"或"asm"开始, 下辖四个部分, 各部 ...
- c# 动态调用WCF方法笔记!
//动态调用wcf方法 string url = "http://localhost:54379/ServiceWCF.svc"; IDoubleService proxy = W ...
- selenium 定位元素
一.单个元素的定位方式: By.className(className))By.cssSelector(selector)By.id(id)By.linkText(linkText)By.name(n ...
- Android中仿IOS提示框的实现
前言 在Android开发中,我们有时需要实现类似IOS的对话框.今天我就来总结下,如何通过自定义的开发来实现类似的功能. 自定义Dialog 我们知道Android中最常用的对话框就是Dialog及 ...