那么好吧,先大致分析下下面第一个链接的思路,不过按照我一贯的习惯,我会先简化代码,简化的文件有
iphone.css:
/* Basic Page Styling ********************************/
#wrapper {
overflow: hidden;
width: 250px;
margin: 10px auto;
border: 1px dotted #eee;
background: #000;
height: 400px;
position: relative;
}
#fake {
overflow: hidden;
width: 2000px;
height: 400px;
}
.content-box {
float: left;
width: 250px;
height: 400px;
display: block;
}
/* Panel Style ***************************************/
.content-box {
color: #d1d1d1;
background: #000;
}
.content-box .inner {margin: 16px;}
iphone.js:
var iPhoneAnimation = function()
{
var initialPosition = null;
var previousPosition = {};
var direction = "";
var width = parseInt($(".content-box:first").width(), 10);
var thirdWidth = parseInt(width / 3, 10);
return {
init: function()
{
},
moveInfo: function(e)
{
var $this = $(this);
//Correct position in current box
var position = e.pageX - $("#wrapper").offset().left;
//Set our initial position, mouse movement now relates to this point
if(initialPosition === null)
{
initialPosition = {left: position};
}
//Relative mouse point
var mouseToPoint = initialPosition.left - position;
//Check what direction mouse moved
if(position > previousPosition.left)
{
direction = "right";
}
else if(position < previousPosition.left)
{
direction = "left";
}
previousPosition = {left: position};
//Move the current container to point
$this.css({
marginLeft: -mouseToPoint
});
},
panelAnimate: function($this)
{
//Grab margin the panel was pulled too
var margin = parseInt($this.css("marginLeft"), 10);
//Look see if there is a previous / next element
var $next = $this.next();
var $prev = $this.prev();
//Index used in indicators
var index = $this.index();
//User pulled left
if(direction === "left")
{
//We have a next element to show
if($next.length && margin < -thirdWidth)
{
$this.animate(
{
marginLeft: -width
},
550,
"easeOutCirc"
);
}
else
{
//Spring back
$this.animate(
{
marginLeft: 0
},
550,
"easeOutCirc"
);
}
}
//User pulled right
if(direction === "right")
{
//We have a previous element to show
if($prev.length && margin > thirdWidth)
{
$prev.animate(
{
marginLeft: 0
},
550,
"easeOutCirc",
function(){
$this.css({
marginLeft: 0
});
}
);
}
else
{
//Spring back
$this.animate({
marginLeft: 0
}, 550, "easeOutCirc");
}
}
},
resetVars: function()
{
initialPosition = null;
}
};
}();
jQuery(function($)
{
//Initialise
iPhoneAnimation.init();
//Mouse down bind move event
$(".content-box").bind(
"mousedown",
function(e)
{
$(this).bind("mousemove", iPhoneAnimation.moveInfo);
}
);
//Unbind mouse event
$(".content-box").bind(
"mouseup",
function(e)
{
var $this = $(this);
$this.unbind("mousemove", iPhoneAnimation.moveInfo);
//Animate the panel
iPhoneAnimation.panelAnimate($this);
//Reset the private var
iPhoneAnimation.resetVars();
}
);
});
先来简单概述下HTML的结构:
<div id="wrapper">
<div id="fake">
<div id="c1" class="content-box">
<div class="inner">
</div>
</div>
<div id="c2" class="content-box">
<div class="inner">
</div>
</div>
<div id="c3" class="content-box">
<div class="inner">
</div>
</div>
<div id="c4" class="content-box">
<div class="inner">
</div>
</div>
</div><!-- div id="fake" -->
</div><!-- div id="wrapper" -->
一个叫wrapper的DIV扮演一个窗口的角色,或者说是mask,遮罩。只有它的范围以内的内容才会被显示出来,因为CSS里指定它的overflow属性为:hidden。
wrapper下面的直属子节点是fake,它其实是个容器,它装着四个被拖拽的类属性为content-box的DIV标签,每个DIV标签有自己的内容,wrapper的大小和每个content-box的大小完全相同,所以同一时刻,只有一个DIV出现。注意,至于这个拖拽的移动效果,是通过直接移动content-box实现的,不是fake,所以fake的overflow属性也是hidden。
下面看下js代码的思路:
首先是这个部分:
var iPhoneAnimation = function()
{
var initialPosition = null;
var previousPosition = {};
var direction = "";
var width = parseInt($(".content-box:first").width(), 10);
var thirdWidth = parseInt(width / 3, 10);
return {
init: function()
{
},
moveInfo: function(e)
{
},
panelAnimate: function($this)
{
},
resetVars: function()
{
}
};
}();
简单看来,它声明了一个名为iPhoneAnimation的函数并即刻呼叫它,但实际上这是Javascript版本的类似高级语言的静态类做法,return前面声明的变量是它的静态成员,比如initialPosition,return区块中的函数则是静态方法,比如init。
至于第二段:
jQuery(function($)
{
//Initialise
iPhoneAnimation.init();
//Mouse down bind move event
$(".content-box").bind(
);
//Unbind mouse event
$(".content-box").bind(
);
});
这是利用jQuery,首先指定DOM加载后执行的动作,其中包括初始化静态类iPhoneAnimation,之后将content-box绑定两个事件,一个是mousedown,一个是mouseup。
在mousedown的处理函数中,将iPhoneAnimation的moveInfo方法绑定给content-box的mousemove事件。而在mouseup的处理函数中,将content-box的mousemove事件取消绑定,并执行panelAnimate方法和resetVars方法。
实际上moveInfo方法做的事情是更新一些静态类iPhoneAnimation的成员变量,还有将content-box根据鼠标位置移动,这样就做到了拖拽的感觉。而panelAnimate方法的任务则是根据成员变量将content-box按照逻辑完成下面的移动,也就是用户释放鼠标(或手指)后将被拖动的content-box弹回原地或弹出wrapper范围而将下一下content-box弹出来,这样就做到了所谓的类似Iphone的swipe效果。最后再通过resetVars方法重置iPhoneAnimation的成员变量来重置状态。
在js代码中加入一些调试代码,追踪某些变量的变化:
moveInfo: function(e)
{
var $this = $(this);
console.log("the dragged div id: " + $this.attr('id'));
//Correct position in current box
var position = e.pageX - $("#wrapper").offset().left;
if(initialPosition)
{
console.log("initialPosition.left before set: " + initialPosition.left);
}
//Set our initial position, mouse movement now relates to this point
if(initialPosition === null)
{
initialPosition = {left: position};
}
console.log("initialPosition.left after set: " + initialPosition.left);
//Relative mouse point
var mouseToPoint = initialPosition.left - position;
console.log("mouseToPoint: " + mouseToPoint);
console.log("direction before set: " + direction);
//Check what direction mouse moved
if(position > previousPosition.left)
{
direction = "right";
}
else if(position < previousPosition.left)
{
direction = "left";
}
console.log("direction after set: " + direction);
console.log("previousPosition.left before set: " + previousPosition.left);
previousPosition = {left: position};
console.log("previousPosition.left after set: " + previousPosition.left);
//Move the current container to point
$this.css({
marginLeft: -mouseToPoint
});
},
panelAnimate: function($this)
{
//Grab margin the panel was pulled too
var margin = parseInt($this.css("marginLeft"), 10);
//Look see if there is a previous / next element
var $next = $this.next();
var $prev = $this.prev();
console.log("next: " + $next.attr('id'));
console.log("prev: " + $prev.attr('id'));
//Index used in indicators
var index = $this.index();
console.log("index: " + index);
//User pulled left
if(direction === "left")
{
//We have a next element to show
if($next.length && margin < -thirdWidth)
{
$this.animate(
{
marginLeft: -width
},
550,
"easeOutCirc"
);
}
else
{
//Spring back
$this.animate(
{
marginLeft: 0
},
550,
"easeOutCirc"
);
}
}
//User pulled right
if(direction === "right")
{
//We have a previous element to show
if($prev.length && margin > thirdWidth)
{
$prev.animate(
{
marginLeft: 0
},
550,
"easeOutCirc",
function(){
$this.css({
marginLeft: 0
});
}
);
}
else
{
//Spring back
$this.animate({
marginLeft: 0
}, 550, "easeOutCirc");
}
}
},
然后,拖住第一个div,稍稍向左移动一点,输出的结果为:
the dragged div id: c1
initialPosition.left after set: 148
mouseToPoint: 0
direction before set:
direction after set:
previousPosition.left before set: undefined
previousPosition.left after set: 148
the dragged div id: c1
initialPosition.left before set: 148
initialPosition.left after set: 148
mouseToPoint: 1
direction before set:
direction after set: left
previousPosition.left before set: 148
previousPosition.left after set: 147
the dragged div id: c1
initialPosition.left before set: 148
initialPosition.left after set: 148
mouseToPoint: 3
direction before set: left
direction after set: left
previousPosition.left before set: 147
previousPosition.left after set: 145
the dragged div id: c1
initialPosition.left before set: 148
initialPosition.left after set: 148
mouseToPoint: 6
direction before set: left
direction after set: left
previousPosition.left before set: 145
previousPosition.left after set: 142
....
the dragged div id: c1
initialPosition.left before set: 148
initialPosition.left after set: 148
mouseToPoint: 22
direction before set: left
direction after set: left
previousPosition.left before set: 127
previousPosition.left after set: 126
next: c2
prev: undefined
index: 0
在moveInfo()方法中,首先计算获得的是position,这个position是用鼠标事件的pageX减去wrapper的offset().left属性,前者是鼠标在整个页面的x坐标位置,后者是wrapper在页面的X轴偏移量,所以,由此得到的position其实是鼠标在wrapper内的本地x坐标,而wrapper是固定不变的,那么position的值就会因为朝着同一方向不停拖动鼠标而增加。就像观测数据中从148到126,鼠标的位移是向左22像素,于是被拖动的content-box的marginLeft属性就被设定为:-22。
现在来说说panelAnimate方法,当然这个方法首先要进行一些逻辑上的检测,比如位移是否超过了宽度的三分之一还有:向左移的话是否有上一个元素向右移的话是否有下一个元素等等。不过不论怎样,当被拖拽的concent-box将被弹回原位时,它的目标位置总是0:
//Spring back
$this.animate(
{
marginLeft: 0
},
550,
"easeOutCirc"
);
即是说,当content-box被正常呈现时,它的x位置是0。而当要把它向左位移时,移动的是被拖拽者本身,它的目标位置是-width,就是-250,可是,当向右移动时,被用animate移动的是被拖拽者的前一个元素,由$prev指向的内容,它的目标位置是0。这同浏览器呈现HTML的原理有关,当你移动一个content-box向左时,它右边的内容会被浏览器自动呈现,然而反之却不行。所以使用这个程序当向左拖拽和向右拖拽时的效果是不同的,当向左时,后面的内容会自动跟进来,向右却不会。另外还请注意:
$prev.animate(
{
marginLeft: 0
},
550,
"easeOutCirc",
function(){
$this.css({
marginLeft: 0
});
}
);
当将被拖拽的前一个元素的位移完成后,执行的动作是将被拖拽元素本身的x位置设置为0,这样它就会自动被放在当前显示的元素后面了。
Refs:
RECREATE IPHONE SWIPE EFFECT USING JQUERY
PhotoSwipe
Using the Swipe Gesture in Flash Using ActionScript 3.0