做过移动端开发的应该多少听过iscroll,iscroll是用来模拟移动端滚动效果的库
iscroll的出现
移动app的布局一般是顶部header+固定高度内容区+footer
这种需求在PC端很简单,header、footer用固定定位,content设置上下margin和overflow:scroll,就ok了,木有啥问题
但是在移动端,首先关于position:fixed,这篇文章对于fixed属性在移动端的问题做了总结;其次,固定高度内容区域的滚动问题,overflow:scroll在移动端是怎样的效果,大家可以写个demo试试,完全没有原生应用那种滚动的流畅感,好在webkit支持了-webkit-overflow-scrolling:touch,于是在ios7以上可以实现区域原生滚动效果了,点这里看效果,ios6和ios5的设备po主没有,谁有的话可以帮忙测测哈
iscroll的出现就是为了实现固定高度区域的原生滚动效果。
原理
iscroll的核心由三大事件组成
touchstart
做一些初始化的操作,包括对触摸的位置,时间,是否在滚动-move等值的初始化
touchmove
首先判断手是否移动距离小于6px,也就是手按着没有动,那么就不做任何处理
move=true
然后计算要滚动到的新的位置newY=oldY+deltaY
判断newY是否已经超出页面边界,是的话降低滚动速度
将页面滚动到新的位置,通过设置transform
每隔300ms重置一次触摸开始时间和结束时间
touchend
首先也是初始化的操作,在手指离开前做了状态保存
判断是否是点击事件(move是否为true),如果是的话,createEvent,然后触发该事件
如果不是点击事件,则惯性滚动到目地位置,
变速滚动是利用requestAnimation来实现的
为什么不用css3呢,因为css3无法实现控制精细的加速减速的效果
源码片段解析
start
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
|
_start
:
function
(
e
)
{
var
that
=
this
,
point
=
hasTouch
?
e
.
touches
[
0
]
:
e
,
matrix
,
x
,
y
,
c1
,
c2
;
if
(
!
that
.
enabled
)
return
;
//执行 自定义的开始滚动前的事件
if
(
that
.
options
.
onBeforeScrollStart
)
that
.
options
.
onBeforeScrollStart
.
call
(
that
,
e
)
;
if
(
that
.
options
.
useTransition
||
that
.
options
.
zoom
)
that
.
_transitionTime
(
0
)
;
//手指是否移动
that
.
moved
=
false
;
that
.
animating
=
false
;
that
.
zoomed
=
false
;
that
.
distX
=
0
;
that
.
distY
=
0
;
that
.
absDistX
=
0
;
that
.
absDistY
=
0
;
that
.
dirX
=
0
;
that
.
dirY
=
0
;
// 针对放大缩小手势的处理
if
(
that
.
options
.
zoom
&&
hasTouch
&&
e
.
touches
.
length
>
1
)
{
c1
=
m
.
abs
(
e
.
touches
[
0
]
.
pageX
-
e
.
touches
[
1
]
.
pageX
)
;
c2
=
m
.
abs
(
e
.
touches
[
0
]
.
pageY
-
e
.
touches
[
1
]
.
pageY
)
;
that
.
touchesDistStart
=
m
.
sqrt
(
c1 *
c1
+
c2 *
c2
)
;
that
.
originX
=
m
.
abs
(
e
.
touches
[
0
]
.
pageX
+
e
.
touches
[
1
]
.
pageX
-
that
.
wrapperOffsetLeft *
2
)
/
2
-
that
.
x
;
that
.
originY
=
m
.
abs
(
e
.
touches
[
0
]
.
pageY
+
e
.
touches
[
1
]
.
pageY
-
that
.
wrapperOffsetTop *
2
)
/
2
-
that
.
y
;
if
(
that
.
options
.
onZoomStart
)
that
.
options
.
onZoomStart
.
call
(
that
,
e
)
;
}
// 针对惯性滚动的处理
if
(
that
.
options
.
momentum
)
{
if
(
that
.
options
.
useTransform
)
{
// Very lame general purpose alternative to CSSMatrix
matrix
=
getComputedStyle
(
that
.
scroller
,
null
)
[
vendor
+
'Transform'
]
.
replace
(
/
[
^
0
-
9
-
.
,
]
/
g
,
''
)
.
split
(
','
)
;
x
=
matrix
[
4
]
*
1
;
y
=
matrix
[
5
]
*
1
;
}
else
{
x
=
getComputedStyle
(
that
.
scroller
,
null
)
.
left
.
replace
(
/
[
^
0
-
9
-
]
/
g
,
''
)
*
1
;
y
=
getComputedStyle
(
that
.
scroller
,
null
)
.
top
.
replace
(
/
[
^
0
-
9
-
]
/
g
,
''
)
*
1
;
}
if
(
x
!=
that
.
x
||
y
!=
that
.
y
)
{
if
(
that
.
options
.
useTransition
)
that
.
_unbind
(
'webkitTransitionEnd'
)
;
else
cancelFrame
(
that
.
aniTime
)
;
that
.
steps
=
[
]
;
that
.
_pos
(
x
,
y
)
;
}
}
//初始化
that
.
absStartX
=
that
.
x
;
// Needed by snap threshold
that
.
absStartY
=
that
.
y
;
//手指触摸的点
that
.
startX
=
that
.
x
;
that
.
startY
=
that
.
y
;
//页面滚动的点
that
.
pointX
=
point
.
pageX
;
that
.
pointY
=
point
.
pageY
;
that
.
startTime
=
e
.
timeStamp
||
(
new
Date
(
)
)
.
getTime
(
)
;
if
(
that
.
options
.
onScrollStart
)
that
.
options
.
onScrollStart
.
call
(
that
,
e
)
;
that
.
_bind
(
MOVE_EV
)
;
that
.
_bind
(
END_EV
)
;
that
.
_bind
(
CANCEL_EV
)
;
}
|
move
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
|
_move
:
function
(
e
)
{
var
that
=
this
,
point
=
hasTouch
?
e
.
touches
[
0
]
:
e
,
deltaX
=
point
.
pageX
-
that
.
pointX
,
deltaY
=
point
.
pageY
-
that
.
pointY
,
newX
=
that
.
x
+
deltaX
,
newY
=
that
.
y
+
deltaY
,
c1
,
c2
,
scale
,
timestamp
=
e
.
timeStamp
||
(
new
Date
(
)
)
.
getTime
(
)
;
//执行自定义的事件
if
(
that
.
options
.
onBeforeScrollMove
)
that
.
options
.
onBeforeScrollMove
.
call
(
that
,
e
)
;
// 如果执行缩放
if
(
that
.
options
.
zoom
&&
hasTouch
&&
e
.
touches
.
length
>
1
)
{
c1
=
m
.
abs
(
e
.
touches
[
0
]
.
pageX
-
e
.
touches
[
1
]
.
pageX
)
;
c2
=
m
.
abs
(
e
.
touches
[
0
]
.
pageY
-
e
.
touches
[
1
]
.
pageY
)
;
that
.
touchesDist
=
m
.
sqrt
(
c1*
c1
+
c2*
c2
)
;
that
.
zoomed
=
true
;
scale
=
1
/
that
.
touchesDistStart *
that
.
touchesDist *
this
.
scale
;
if
(
scale
<
that
.
options
.
zoomMin
)
scale
=
0.5
*
that
.
options
.
zoomMin *
Math
.
pow
(
2.0
,
scale
/
that
.
options
.
zoomMin
)
;
else
if
(
scale
>
that
.
options
.
zoomMax
)
scale
=
2.0
*
that
.
options
.
zoomMax *
Math
.
pow
(
0.5
,
that
.
options
.
zoomMax
/
scale
)
;
that
.
lastScale
=
scale
/
this
.
scale
;
newX
=
this
.
originX
-
this
.
originX *
that
.
lastScale
+
this
.
x
,
newY
=
this
.
originY
-
this
.
originY *
that
.
lastScale
+
this
.
y
;
this
.
scroller
.
style
[
vendor
+
'Transform'
]
=
trnOpen
+
newX
+
'px,'
+
newY
+
'px'
+
trnClose
+
' scale('
+
scale
+
')'
;
if
(
that
.
options
.
onZoom
)
that
.
options
.
onZoom
.
call
(
that
,
e
)
;
return
;
}
that
.
pointX
=
point
.
pageX
;
that
.
pointY
=
point
.
pageY
;
// 如果页面滚动超过了界限,降低滚动速度,其实也就是减小newX newY的指
if
(
newX
>
0
||
newX
<
that
.
maxScrollX
)
{
newX
=
that
.
options
.
bounce
?
that
.
x
+
(
deltaX
/
2
)
:
newX
>=
0
||
that
.
maxScrollX
>=
0
?
0
:
that
.
maxScrollX
;
}
if
(
newY
>
that
.
minScrollY
||
newY
<
that
.
maxScrollY
)
{
newY
=
that
.
options
.
bounce
?
that
.
y
+
(
deltaY
/
2
)
:
newY
>=
that
.
minScrollY
||
that
.
maxScrollY
>=
0
?
that
.
minScrollY
:
that
.
maxScrollY
;
}
//手指移动在6px以内,认为没有移动
if
(
that
.
absDistX
<
6
&&
that
.
absDistY
<
6
)
{
that
.
distX
+=
deltaX
;
that
.
distY
+=
deltaY
;
that
.
absDistX
=
m
.
abs
(
that
.
distX
)
;
that
.
absDistY
=
m
.
abs
(
that
.
distY
)
;
return
;
}
// Lock direction
if
(
that
.
options
.
lockDirection
)
{
if
(
that
.
absDistX
>
that
.
absDistY
+
5
)
{
newY
=
that
.
y
;
deltaY
=
0
;
}
else
if
(
that
.
absDistY
>
that
.
absDistX
+
5
)
{
newX
=
that
.
x
;
deltaX
=
0
;
}
}
//手指在移动,页面开始跟着滚动 _pos韩束
that
.
moved
=
true
;
that
.
_pos
(
newX
,
newY
)
;
that
.
dirX
=
deltaX
>
0
?
-
1
:
deltaX
<
0
?
1
:
0
;
that
.
dirY
=
deltaY
>
0
?
-
1
:
deltaY
<
0
?
1
:
0
;
if
(
timestamp
-
that
.
startTime
>
300
)
{
that
.
startTime
=
timestamp
;
that
.
startX
=
that
.
x
;
that
.
startY
=
that
.
y
;
}
if
(
that
.
options
.
onScrollMove
)
that
.
options
.
onScrollMove
.
call
(
that
,
e
)
;
}
|
end
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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
_end
:
function
(
e
)
{
if
(
hasTouch
&&
e
.
touches
.
length
!=
0
)
return
;
var
that
=
this
,
point
=
hasTouch
?
e
.
changedTouches
[
0
]
:
e
,
target
,
ev
,
momentumX
=
{
dist
:
0
,
time
:
0
}
,
momentumY
=
{
dist
:
0
,
time
:
0
}
,
duration
=
(
e
.
timeStamp
||
(
new
Date
(
)
)
.
getTime
(
)
)
-
that
.
startTime
,
newPosX
=
that
.
x
,
newPosY
=
that
.
y
,
distX
,
distY
,
newDuration
,
snap
,
scale
;
that
.
_unbind
(
MOVE_EV
)
;
that
.
_unbind
(
END_EV
)
;
that
.
_unbind
(
CANCEL_EV
)
;
if
(
that
.
options
.
onBeforeScrollEnd
)
that
.
options
.
onBeforeScrollEnd
.
call
(
that
,
e
)
;
if
(
that
.
zoomed
)
{
scale
=
that
.
scale *
that
.
lastScale
;
scale
=
Math
.
max
(
that
.
options
.
zoomMin
,
scale
)
;
scale
=
Math
.
min
(
that
.
options
.
zoomMax
,
scale
)
;
that
.
lastScale
=
scale
/
that
.
scale
;
that
.
scale
=
scale
;
that
.
x
=
that
.
originX
-
that
.
originX *
that
.
lastScale
+
that
.
x
;
that
.
y
=
that
.
originY
-
that
.
originY *
that
.
lastScale
+
that
.
y
;
that
.
scroller
.
style
[
vendor
+
'TransitionDuration'
]
=
'200ms'
;
that
.
scroller
.
style
[
vendor
+
'Transform'
]
=
trnOpen
+
that
.
x
+
'px,'
+
that
.
y
+
'px'
+
trnClose
+
' scale('
+
that
.
scale
+
')'
;
that
.
zoomed
=
false
;
that
.
refresh
(
)
;
if
(
that
.
options
.
onZoomEnd
)
that
.
options
.
onZoomEnd
.
call
(
that
,
e
)
;
return
;
}
// 如果手指没有做滚动操作,而是点击操作
if
(
!
that
.
moved
)
{
if
(
hasTouch
)
{
//双击操作且zoom是开启的,放大
if
(
that
.
doubleTapTimer
&&
that
.
options
.
zoom
)
{
// Double tapped
clearTimeout
(
that
.
doubleTapTimer
)
;
that
.
doubleTapTimer
=
null
;
if
(
that
.
options
.
onZoomStart
)
that
.
options
.
onZoomStart
.
call
(
that
,
e
)
;
that
.
zoom
(
that
.
pointX
,
that
.
pointY
,
that
.
scale
==
1
?
that
.
options
.
doubleTapZoom
:
1
)
;
if
(
that
.
options
.
onZoomEnd
)
{
setTimeout
(
function
(
)
{
that
.
options
.
onZoomEnd
.
call
(
that
,
e
)
;
}
,
200
)
;
// 200 is default zoom duration
}
}
else
{
that
.
doubleTapTimer
=
setTimeout
(
function
(
)
{
that
.
doubleTapTimer
=
null
;
// Find the last touched element
target
=
point
.
target
;
while
(
target
.
nodeType
!=
1
)
target
=
target
.
parentNode
;
if
(
target
.
tagName
!=
'SELECT'
&&
target
.
tagName
!=
'INPUT'
&&
target
.
tagName
!=
'TEXTAREA'
)
{
//定义click事件,并触发
ev
=
document
.
createEvent
(
'MouseEvents'
)
;
ev
.
initMouseEvent
(
'click'
,
true
,
true
,
e
.
view
,
1
,
point
.
screenX
,
point
.
screenY
,
point
.
clientX
,
point
.
clientY
,
e
.
ctrlKey
,
e
.
altKey
,
e
.
shiftKey
,
e
.
metaKey
,
0
,
null
)
;
ev
.
_fake
=
true
;
target
.
dispatchEvent
(
ev
)
;
}
}
,
that
.
options
.
zoom
?
250
:
0
)
;
}
}
that
.
_resetPos
(
200
)
;
if
(
that
.
options
.
onTouchEnd
)
that
.
options
.
onTouchEnd
.
call
(
that
,
e
)
;
return
;
}
//对惯性滚动的处理
if
(
duration
<
300
&&
that
.
options
.
momentum
)
{
momentumX
=
newPosX
?
that
.
_momentum
(
newPosX
-
that
.
startX
,
duration
,
-
that
.
x
,
that
.
scrollerW
-
that
.
wrapperW
+
that
.
x
,
that
.
options
.
bounce
?
that
.
wrapperW
:
0
)
:
momentumX
;
momentumY
=
newPosY
?
that
.
_momentum
(
newPosY
-
that
.
startY
,
duration
,
-
that
.
y
,
(
that
.
maxScrollY
<
0
?
that
.
scrollerH
-
that
.
wrapperH
+
that
.
y
-
that
.
minScrollY
:
0
)
,
that
.
options
.
bounce
?
that
.
wrapperH
:
0
)
:
momentumY
;
newPosX
=
that
.
x
+
momentumX
.
dist
;
newPosY
=
that
.
y
+
momentumY
.
dist
;
if
(
(
that
.
x
>
0
&&
newPosX
>
0
)
||
(
that
.
x
<
that
.
maxScrollX
&&
newPosX
<
that
.
maxScrollX
)
)
momentumX
=
{
dist
:
0
,
time
:
0
}
;
if
(
(
that
.
y
>
that
.
minScrollY
&&
newPosY
>
that
.
minScrollY
)
||
(
that
.
y
<
that
.
maxScrollY
&&
newPosY
<
that
.
maxScrollY
)
)
momentumY
=
{
dist
:
0
,
time
:
0
}
;
}
if
(
momentumX
.
dist
||
momentumY
.
dist
)
{
newDuration
=
m
.
max
(
m
.
max
(
momentumX
.
time
,
momentumY
.
time
)
,
10
)
;
// Do we need to snap?
if
(
that
.
options
.
snap
)
{
distX
=
newPosX
-
that
.
absStartX
;
distY
=
newPosY
-
that
.
absStartY
;
if
(
m
.
abs
(
distX
)
<
that
.
options
.
snapThreshold
&&
m
.
abs
(
distY
)
<
that
.
options
.
snapThreshold
)
{
that
.
scrollTo
(
that
.
absStartX
,
that
.
absStartY
,
200
)
;
}
else
{
snap
=
that
.
_snap
(
newPosX
,
newPosY
)
;
newPosX
=
snap
.
x
;
newPosY
=
snap
.
y
;
newDuration
=
m
.
max
(
snap
.
time
,
newDuration
)
;
}
}
that
.
scrollTo
(
m
.
round
(
newPosX
)
,
m
.
round
(
newPosY
)
,
newDuration
)
;
if
(
that
.
options
.
onTouchEnd
)
that
.
options
.
onTouchEnd
.
call
(
that
,
e
)
;
return
;
}
// Do we need to snap?
if
(
that
.
options
.
snap
)
{
distX
=
newPosX
-
that
.
absStartX
;
distY
=
newPosY
-
that
.
absStartY
;
if
(
m
.
abs
(
distX
)
<
that
.
options
.
snapThreshold
&&
m
.
abs
(
distY
)
<
that
.
options
.
snapThreshold
)
that
.
scrollTo
(
that
.
absStartX
,
that
.
absStartY
,
200
)
;
else
{
snap
=
that
.
_snap
(
that
.
x
,
that
.
y
)
;
if
(
snap
.
x
!=
that
.
x
||
snap
.
y
!=
that
.
y
)
that
.
scrollTo
(
snap
.
x
,
snap
.
y
,
snap
.
time
)
;
}
if
(
that
.
options
.
onTouchEnd
)
that
.
options
.
onTouchEnd
.
call
(
that
,
e
)
;
return
;
}
that
.
_resetPos
(
200
)
;
if
(
that
.
options
.
onTouchEnd
)
that
.
options
.
onTouchEnd
.
call
(
that
,
e
)
;
}
|