昨天,有位叫学智的哥们(和我大学一个同学(绰号-法师)同名不同姓)要做一个手机上的页面切换效果,虽然自己的期望和实际的效果相差并不是很大,但法师追求完美,眼睛里揉不得沙子的秉性,把他逼得急躁,惶恐,甚至有些迷失自我,不得不为解决一个bug不惜搞出十个bug,法师快哭了…… 此时,老天有眼,曾经逼迫他的伟大秉性尚且给他留了点智商,学智想起了我们群,对!就是我们群——HTML5移动开发No.1,群号219118183,欢迎加入!(请原谅我这么狗血的开头 -_-‘ )
以下是对话节选:
CodingSerf: 亲,不知道您要什么效果呢?
学智:就是手指往上滑,下一页就会滑上来盖住这页;向下滑就上一页滑下来盖住这页。
CodingSerf: 亲,这不就是个slider么?
学智: 那应该怎么整哇
CodingSerf: 你也不要哭,静下心来好好想想
于是,我静下心来好好想了想,写了一个插件,为了纪念法师,插件就叫touchslider吧。因为学智用到了Zepto,这也是我喜欢用的移动端lib,所以这是个Zepto的插件。当然在我看来这更像是个移动端页面切换的效果,总之逻辑是差不多的。
插件写的很了草,支持的接口也不多,为什么呢?因为自己写插件够用就好,你随便网上找个插件NB的不行,功能提供一大堆,但你在某个项目中用到的就是那么一两个接口,而你却不得不把他们都load下来,所以有时你也会感觉jQuery太臃肿,很多API都不会用到,于是Zepto的模块化就显出了优势。zepto.js主要提供Dom相关的操作,你想用动画,你想用tap事件?好,zepto.js里是没有这些模块的,你需要加载fx.js和touch.js。
touchslider插件里会用到animate方法来执行动画,所以要依赖zepto的fx.js模块。
之前,学智的需求是要上下纵向滑动的,我们可以放一个接口让插件接受左右横向滑动,但是代码却不会增加多少,因为还是用一样的逻辑,只是一个标识位的不同。
此外,我们可以再开放一个接口来设置当用户滑动多少百分比(0.0~1.0)后才会触发slider,否则就还原到滑动前的位置。
下面是两个Demo页面,在PC端查看记得打开控制台的触摸屏模拟器(emulate touch screen):
插件使用方法如下:
<script type="text/javascript" src="zepto.touchSlider.js"></script>
<script type="text/javascript">
$(function(){
$('.main').touchSlider({direction: 'h',itemSelector:'.page',slidePercent:0.3});
});
</script>
<div class="main">
<div class="page page_1">page<p>1</p><a href="http://www.codingserf.com" target="_blank">CodingSerf.com</a></div>
<div class="page page_2">page<p>2</p><a href="http://www.codingserf.com" target="_blank">CodingSerf.com</a></div>
<div class="page page_3">page<p>3</p><a href="http://www.codingserf.com" target="_blank">CodingSerf.com</a></div>
<div class="page page_4">page<p>4</p><a href="http://www.codingserf.com" target="_blank">CodingSerf.com</a></div>
<div class="page page_5">page<p>5</p><a href="http://www.codingserf.com" target="_blank">CodingSerf.com</a></div>
<div class="page page_6">page<p>6</p><a href="http://www.codingserf.com" target="_blank">CodingSerf.com</a></div>
</div>
以下是插件的源码(你也可以在github上查看最新版本):
// zepto.touchslider.js
// v 1.0
// David
// http://www.CodingSerf.com
;(function($){
//干掉页面bounce效果
$(document).on('touchmove',function(e){
e.preventDefault();
});
$.fn.touchSlider = function(option){
var opts = $.extend({}, $.fn.touchSlider.defaults, option), //配置选项
$slider = this,
$items = $slider.find(opts.itemSelector), //获取子元素数组
sliderStylePosition = $slider.css('position'),
sliderHeight = $slider.height(),
sliderWidth = $slider.width(),
step = 0,
curItemIndex = 0,
oldPosition = 0,
backMove = false,
forwardMove = false,
backFirst = false,
forwardLast = false,
isAnimate = false,
isDirectionV = (opts.direction == 'v'),
distance = isDirectionV ? sliderHeight : sliderWidth;
//初始化
$slider.css({'background-color':'#000'})
sliderStylePosition == 'absolute' || sliderStylePosition == 'fixed' || ($slider.css({'position':'relative'})); //设置slider容器的position值
$items.each(function(i,o){
$items.eq(i).css({
'position':'absolute',
'top': isDirectionV ? sliderHeight+'px' : 0,
'left': isDirectionV ? 0 : sliderWidth +'px',
'z-index': '500'
});
}); //遍历子元素设置样式
$items.eq(0).css(isDirectionV ? 'top' : 'left', 0); //重置第一个子元素的样式
//事件注册
$slider.on('touchstart',function(e){
var touch = e.touches[0];
oldPosition = isDirectionV ? touch.clientY : touch.clientX;
step = 0;
backMove = false;
forwardMove = false;
backFirst = false;
forwardLast = false;
}).on('touchmove',function(e){
var touch = e.touches[0],
curPosition = isDirectionV ? touch.clientY : touch.clientX,
gap = curPosition - oldPosition,
moveItemIndex=0,
newPosition = 0;
step += gap;
//向上/左正向滑
if(gap<0 && !isAnimate){
forwardLast = curItemIndex == $items.length-1 && !backMove ? true : false;
if(backMove){//先向下/右逆向滑,过程中不放手再向上/左正向滑
newPosition = -distance+step;
moveItemIndex = curItemIndex==0 ? curItemIndex : curItemIndex-1;
moveItem(moveItemIndex, newPosition);
}else if(curItemIndex!==$items.length-1 && !backFirst){
newPosition = distance+step;
moveItemIndex = curItemIndex+1;
moveItem(moveItemIndex, newPosition);
forwardMove = true;
}
}
//向下/右逆向滑
if(gap>0 && !isAnimate){
backFirst = curItemIndex == 0 && !forwardMove ? true : false;
if(forwardMove){//先向上/左正向滑,过程中不放手再向下/右逆向滑
newPosition = distance+step;
moveItemIndex = curItemIndex==$items.length-1 ? curItemIndex : curItemIndex+1;
moveItem(moveItemIndex, newPosition);
}else if(curItemIndex!==0 && !forwardLast){
newPosition = -distance+step;
moveItemIndex = curItemIndex-1;
backMove = true;
moveItem(moveItemIndex, newPosition);
}
}
//更新坐标
oldPosition = curPosition;
}).on('touchend',function(e){
if(forwardMove){
animateItem(1);
}
if(backMove){
animateItem(-1);
}
});
//放手后自动滑向目标位置, flag控制滑动方向
function animateItem(flag){
isAnimate = true;
var percent = Math.abs(step)/distance,
animTarget = percent>opts.slidePercent ? '0px' : distance*flag+'px',
animProperty = isDirectionV ? {'top': animTarget} : {'left': animTarget},
oldTarget = -distance*flag+'px',
oldProperty = isDirectionV ? {'top': oldTarget} : {'left': oldTarget};
$items.eq(curItemIndex+flag).animate(animProperty,250,'ease-in',function(){
if(percent>opts.slidePercent){
$items.eq(curItemIndex).css(oldProperty).css('opacity','');
$items.eq(curItemIndex+flag).css({'transition':'','z-index':500});
curItemIndex += flag;
opts.onMoveEnd(curItemIndex);
}else{
$items.eq(curItemIndex).css('opacity','');
}
isAnimate = false;
});
}
//设置随手被拖动的坐标
function moveItem(moveItemIndex, newPosition){
$items.eq(curItemIndex).css('opacity',1-Math.abs(step)/distance);
$items.eq(moveItemIndex).css(isDirectionV ? 'top' : 'left',newPosition+'px').css({'z-index':1000});
}
//链式返回
return this;
};
$.fn.touchSlider.defaults = {
direction: 'h', //'h' 纵向,'v' 横向
slidePercent: 0.3, //拖动超过多少百分比后才翻页
itemSelector: 'div', //子元素选择器
onMoveEnd: function(index){ //滑动结束后事件
//console.log(index);
}
};
})(Zepto);
左右横向滑动
上下纵向滑动

5235

被折叠的 条评论
为什么被折叠?



