写在前面的话,以手机浏览的朋友,因手机屏幕不是很宽,如果存在代码看不全的情况,所有的代码区是可以左右滑动的,当然,你也可以点击代码区右上角从左往右数的第二个按钮,让代码换行显示。
之前做一个门户型的手机网站,要做一个类似今日头条那种可以拖动的导航,就是看上去像是手机原生拖动网页上下滚动的那种效果,拖得快,滚得快,拖得慢,滚得慢,放开手指后还要有一定的缓冲距离。由于之前做手机页面都是用的简单的click事件,没用到拖动,所以刚开始的时候也遇到了很多问题,因为手机端没有什么mousedown,mousemove,mouseup等事件,取而代之的是touchstart,touchmove,touchend,但问题时手机上屏幕上可以同时放多个手指,所以touch事件和mouse事件肯定有不一样的地方,我也是到处找资料,看了很久才勉强会用。由于今天的重点是滑动导航,touch的事就先不说了,有兴趣的同志可以看一下这篇文章,浅谈javascript的Touch事件。
下面进入今天的主题,navscroll。首先,在代码实现之前,我们先弄明白它的原理是什么,只有原理搞清楚了,我们代码实现起来才会流畅,理解起来也会懂得更快。其实它的原理就跟幻灯片是一回事,先定义一个外层盒子,超出部分隐藏,position设置为相对定位,然后再在里面添加内容,设置绝对定位,动态改变里面的left值,就能实现其效果。我们以图说明:
最外层盒子宽度为400,里面可以滚动的内容宽度为1000,则可以滚动的最大距离,我们设为maxLeft,它等于1000-400=600。当导航滚到最右边时,它的left值则为-600px,如下图所示:
针对现在的移动设备,浏览器几乎全兼容css3,所以我们在真正实现的时候,也可以用translateX来代替left。我这里就只介绍改变left的方式了,喜欢的朋友可以自己尝试一下translateX的做法。
接下来,我们再来看一下怎么判断拖动的快慢。其实道理也很简单,就像我们跑步一样,比如第一分钟你在100米的位置,第二分钟你在400米的位置,那你这一分钟的速度就是400-100=300米/分钟,而第三分钟时,你的位置在800米的位置,那么第二分钟到第三分钟你的速度就是800-400=400米/分钟,说明你是越来越快了。我们的拖动也是一样,假设第一次监听到拖动时手指的位置在90px(图中1)处,而第二次监听到拖动时手指的位置在180px(图中2)处,那么你的速度就是90px/次拖动。如下所示:
然后,我们再来考虑如何让导航在手指放开后还能缓慢移动一段距离。这个就跟我们踢足球一样,球踢出去之后,由于受到地面的摩擦,它的速度会越来越小,越来越小,最终变为0。所以,当我们放开手指后,也要让拖动的元素的速度越来越小,但手机上的这些东西并没有所谓的什么摩擦力,我们唯一能采用的办法就是每次都让它的速度乘以一个小于1的数,但又必须大于0,因为乘以0就为0了,千万别整个小于0的数啊,不然到时候它就要往反方向跑了。我们一般叫这个数为摩擦系数,我们假设刚开始的速度为100,摩擦系数为0.9,那么速度依次为100*0.9=90,90*0.9=81,81*0.9=72.9,72.9*0.9=65.61………,这样一直下去,但这样它永远不会等于0,为此,我们可以规定,当速度小于某一个值的时候,就把它视为0,不动了。
最主要的几个点已经讲完了,再简单的说两个技巧上的东西:
1、我们的导航元素有可能并不是固定的,可能有10个栏目,也有可能有20个栏目,但是对于ul里面的li元素,我们又必须设置float为left才会并排,如果不给ul一个合适的宽度,装不下的它会自动往下挤,但我们ul的宽度写死了又不好,解决办法是把ul的display设置为table-cell,再把里面的li的display设置为inline-block,但inline-block的元素默认有一点间距,可以通过把ul的font-size设置为0来清除,不过这样的话必须重写li里面的font-size。
2、导航放到页面上之后,我们可能会实时编辑导航,增加和删除,那么ul的宽度就随时可能会变,处理办法是在每次touchstart的时候都重新获取ul的宽度。
剩下的还有一些细节上的东西,我就不再一一说明了,看代码就行。作为一个前端功底不是怎么样的人来说,代码肯定是还有些问题的,希望大家指正,如果你有更好的方案,欢迎分享。
下面直接上代码
页面html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <title>模拟滑动导航</title> <meta name="description" content=""> <meta name="viewport" content="initial-scale=1, maximum-scale=1"> <link rel="shortcut icon" href="http://m.sui.taobao.org/favicon.ico"> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="format-detection" content="telephone=no"> <style> body{ font-size:14px; } body, div, ul, ol, li, p { margin: 0; padding: 0; display: block; } a, a:hover, a:active, a:visited{ text-decoration: none; color:#fff; } .topbar{ position: relative; width: 100%; line-height: 2.2rem; height: 2.2rem; overflow: hidden; } .topnavul { position: absolute; left: 0; top: 0; font-size: 0; display: table-cell; white-space: nowrap; list-style: none; background: #3e3e3e; } .topnavul li { display: inline-block; line-height: 2.2rem; font-size: .85rem; padding: 0 .4rem; } .topnavul li:last-child { margin-right: 0; } </style> </head> <body> <!--顶部工具栏部分 start--> <div class="topbar clearfix" id="topbar"> <div class="topnav" id="topnav"> <ul class="topnavul"> <li class="active"><a href="#">首页</a></li> <li><a href="#">中央政策</a></li> <li><a href="#">重大新闻</a></li> <li><a href="#">时政新闻</a></li> <li><a href="#">军事才艺</a></li> <li><a href="#">视频</a></li> <li><a href="#">娱乐头条</a></li> <li><a href="#">热门综艺</a></li> <li><a href="#">军事才艺</a></li> <li><a href="#">视频</a></li> <li><a href="#">娱乐头条</a></li> <li><a href="#">军事才艺</a></li> <li><a href="#">视频</a></li> <li><a href="#">娱乐头条</a></li> </ul> </div> </div> <!--这两个js文件可以用jquery代替,不知道这两个js的,自行百度一下是干嘛的--> <script type="text/javascript" src="js/zepto.js"></script> <script type="text/javascript" src="js/fx.js"></script> <script type="text/javascript" src="js/simulateTouchScroll.js"></script> <script type="text/javascript"> $(function(){ //设置导航可滚动 $("#topnav").navScroll(); //这里并不会在控制台输出0,因为我们在上面的拖动事件中阻止了事件冒泡 $("#topbar").on('touchstart',function(){ console.log(0); }) }) </script> </body> </html> |
js文件simulateTouchScroll.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
$.fn.simulateTouchScroll = function(){ if(this.length == 0)return; var $scroller = this, $innerScroll = this.children(), downX = 0, downLeft = 0, prevX = 0, moveX = 0, iSpeedX = 0, ow = $scroller.offset().width, iw = $innerScroll.offset().width, maxLeft = iw - ow, timer = null; var bBtn = false; $innerScroll.on("touchstart", function (ev) { if(timer)clearInterval(timer); ev.stopPropagation(); var touchs = ev.changedTouches[0]; downX = touchs.pageX; downLeft = $(this).position().left; prevX = touchs.pageX; }); $innerScroll.on("touchmove",function (ev) { ev.stopPropagation(); var touchs = ev.changedTouches[0]; iSpeedX = touchs.pageX - prevX; prevX = touchs.pageX; moveX = touchs.pageX - downX; if (Math.abs(moveX) < 10) return; var newLeft = downLeft + moveX; if ($(this).position().left >= 0) { if(bBtn){ bBtn = false; downX = touchs.pageX; } //$(this).css("left",(touchs.pageX - downX)/3 + 'px'); $(this).css("left",downLeft + moveX/3 + 'px'); } else if ($(this).position().left <= -maxLeft) { if(bBtn){ bBtn = false; downX = touchs.pageX; } //$(this).css("left",(touchs.pageX - downX)/3 - maxLeft + 'px'); $(this).css("left", moveX/3 - maxLeft + 'px'); } $(this).css("left", newLeft + 'px'); }); $innerScroll.on("touchend",function () { var _this = $(this); if (Math.abs(moveX) < 10) return; timer = setInterval(function () { if (Math.abs(iSpeedX) <= 1 || _this.position().left >= 50 || _this.position().left <= -maxLeft - 50) { if (timer)clearInterval(timer); iSpeedX = 0; if (_this.position().left > 0) { _this.animate({left: 0}, 400, 'ease-out'); } else if (_this.position().left < -maxLeft) { _this.animate({left: -maxLeft + "px"}, 400, 'ease-out'); } } else { iSpeedX = iSpeedX * 0.95; _this.css("left", _this.position().left + iSpeedX + 'px'); } }, 13); }); }; |
上面的代码没加注释,如果需要看注释的朋友,请移步本代码git,自行下载查看,git另附压缩版,地址https://github.com/veznlee/simplePlugin
发表评论