【iScroll源码学习00】模拟iScroll

时间:2021-08-23 04:04:27

前言

相信对移动端有了解的朋友对iScroll这个库非常熟悉吧,今天我们就来说下我们移动页面的iScroll化

iScroll是我们必学框架之一,我们这次先根据iScroll功能自己实现其功能,然后再学习iScroll源码

下面先给出iScroll官方的例子和源码,要看效果的朋友自己去看吧:https://github.com/cubiq/iscroll

本人能力有限,文中有误请提出

viewport

在移动端新出了一个属性叫做“viewport”,这个便是我们手机上的虚拟视口(viewport),也就是视觉窗口,显示区域

移动设备的显示区域比电脑小得多(但也方便得多),为了让手机显示的更加友好,Apple提供了一个方法:在浏览器定义了viewport meta标签

他的作用就是创建一个虚拟窗口,这个虚拟窗口接近桌面浏览器(980px),事实上viewport就是用以放大缩小网页内容

<meta name=”viewport”
content=”width=device-width, initial-scale=1, maximum-scale=1″> width:控制 viewport 的大小,可以指定的一个值,如果 600,或者特殊的值,如 device-width 为设备的宽度(单位为缩放为 100% 时的 CSS 的像素)。
height:和 width 相对应,指定高度。
initial-scale:初始缩放比例,也即是当页面第一次 load 的时候缩放比例。
maximum-scale:允许用户缩放到的最大比例。
minimum-scale:允许用户缩放到的最小比例。
user-scalable:用户是否可以手动缩放

visual/layout viewport

(此处引用——原文出处: quirksmode   译文出处: Zhao Yuhao

想象我们有个房间,我们可以控制房间大小,现在我们站在他窗户面前,正对着窗户的墙壁涂满了壁画,我们走到窗口一米的位置往房间看(假设房子很大)

我们能看到整个壁画,但是有点小,于是我们缩小房子就能看清细节了,这里的窗户就是visual viewport 墙壁就是layout viewport

【iScroll源码学习00】模拟iScroll【iScroll源码学习00】模拟iScroll

对于css布局,特别是用宽度百分比做排版时候,比率是按照layout viewport计算,也就是说一个div相对宽度50%,用户在手机浏览器放大缩小

div宽度不会一直显示相对窗口50%,整个div可能铺满窗口小到看不到

我们这里的viewport就相当于放大和缩小房间,找到一个合适的平衡点,让我们的网页在手机上更友好的显示

① 假如我们现在又个简单的页面,不给div设置宽度(默认是layout100%——980px),所以显示效果为:

【iScroll源码学习00】模拟iScroll

② 用户通过放大网页比例,缩小visual viewport的值,相对而言用户就能看清楚div的内容了,但是layout viewport本身未发现变化(所以可能出现滚动条)

【iScroll源码学习00】模拟iScroll

③ 这个时候上文中的device-width就派上了用场,他可以将layout viewport的像素设置为设备的像素,这样的话:

visual viewport=layout viewport=screen width,这个体验就比较好了

【iScroll源码学习00】模拟iScroll

device-width

以上知识点暂时到这,这里我们补充几个知识点:

① 宽度问题:

layout viewport 的长宽 (document.documentElement.clientWidth / document.documentElement.clientHeight)

visual viewport 的长宽 (window.innerWidth / window.innerHeight)

② 设备像素

screen.width/height

③ Media queries,这个是html5新增特性,可以根据device-width(设备宽度,screen width)来确定显示不同的CSS

1. visual viewport 宽度 : 默认980 实际大小与缩放比例相关,可以通过meta的viewport属性修改
2. layout viewport 宽度 : 980
3. screen.width :320

我们这里来重新理解下device-width这个属性,这里提供一段代码,两个截图:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta name="viewport" content="width=device-width">
</head>
<body>
方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨
</body>
</html>
<meta name="viewport" content="width=100">
<!-- 这里以此给width赋值:① 默认情况/② device-width/③ 100 ->

【iScroll源码学习00】模拟iScroll

为何出现iScroll

fixed与噩梦

最初的ios与android并不支持fixed属性,因为我们的手机有一个叫viewport的东西这个大家都知道了,fixed位置是相对整个页面的固定位置

页面中的页面没什么变化,只不过在viewport下变大了,而且我们移动的是viewport,网页并未跟着滚动,于是我们移动的事实上是viewport,

而我们viewport移动并不会让我们fixed元素跟着变化,因为他是相对于手机屏幕的,所以就不支持了,反正后面这个问题被修复了

但是据我的经验来说,就是现在ios6、7或者android高版本fixed仍然不是那么好使,移动端的fixed就跟ie7的float似的,让人想哭

特别是当你点击文本框时候看到键盘上来了,页面错位了,一股想扔手机之情油然而生

加之想要用户自动升级手机浏览器什么的仍然不现实,所以iScroll诞生了,这是iScroll诞生的主要原因(我是这么认为的)

overflow: auto

既然fixed不好使,那么就头尾固定,中间body部分使用overflow属性吧,但是可恨的是overflow属性仍然不好使!!!

我们这里来做一个demo:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<style type="text/css">
#header { width: 100%; height: 50px; position: absolute; top: 0; left: 0; }
#footer { width: 100%; height: 50px; position: absolute; bottom: 0; left: 0; }
#body { height: 180px; margin: 60px 0; overflow: auto; }
div { border: 1px solid black; }
</style>
</head>
<body>
<div id="header">header</div>
<div id="body">方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方
<input type="text" />
法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨 方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方
<input type="text" />
法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨方法是多少的发生的范德萨 </div>
<div id="footer">footer</div> </body>
</html>

其实这个属性也是可以实现三行布局的基本功能的,但是有以下几个问题:

① 没有滚动条

② 滚动不顺畅

③ 手机浏览器支持良莠不齐

但是这些缺点也不能掩盖他最大的优点:原生性!!!原生的就是最好的,如果哪天这个属性升级的话,前途就好了

页面切换动画

要伪装APP,页面切换动画必不可少,但是如果中间部分不固定的话就会碰到另外一个令人头疼的问题:

长短页切换问题,想象下几个长短不一的页面切换会有多丑呢???

PS:这也是我现在遇到的问题

基于以上原因,所以出现了iScroll这样的实用库,当然,以上只是个人猜想......

实现iScroll功能

基本dom结构

以上扯了那么多,与本文的最初目的关系其实不大,我们的主要目的还是得先实现一个简单的iScroll功能才行(依赖zepto)

 <html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<style type="text/css">
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td, hr, button, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { margin: 0; padding: 0; }
body { font: normal 14px/1.5 "Arial" , "Lucida Grande" ,Verdana, "Microsoft YaHei" , "hei"; -webkit-font-smoothing: antialiased; color: #000; background: #f5f5f5; }
header { position: absolute; top: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; }
footer { position: absolute; bottom: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; }
h1 { display: block; font-size: 2em; font-weight: bold; font-weight: 500; text-align: center; color: White; }
#body { background: #fff; border: 1px solid #cfcfcf; width: 96%; height: 100%; margin: 50px auto; padding: 4px; }
</style>
</head>
<body>
<header id="header">
<h1>
Header</h1>
</header>
<div id="body">
body
</div>
<footer>
<h1>
Footer</h1>
</footer>
</body>
</html>

我们根据iScroll的动作先写下以下代码:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<style type="text/css">
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td, hr, button, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { margin: 0; padding: 0; }
body { font: normal 14px/1.5 "Arial" , "Lucida Grande" ,Verdana, "Microsoft YaHei" , "hei"; -webkit-font-smoothing: antialiased; color: #000; background: #ccc; }
header { position: absolute; top: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; }
footer { position: absolute; bottom: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; }
h1 { display: block; font-size: 2em; font-weight: bold; font-weight: 500; text-align: center; color: White; } #body { position: absolute; top: 50px; bottom: 50px; background: #fff; width: 100%; overflow: hidden; }
#wrapper { width: 100%; } #wrapper li { padding: 0 10px; height: 40px; line-height: 40px; border-bottom: 1px solid #ccc; border-top: 1px solid #fff; background-color: #fafafa; font-size: 14px; }
</style>
</head>
<body>
<header id="header">
<h1>
Header</h1>
</header>
<div id="body">
<div id="wrapper">
body
<ul>
<li>Pretty row 1</li>
<li>Pretty row 2</li>
<li>Pretty row 3</li>
<li>Pretty row 4</li>
<li>
<input type="text"></li>
<li>Pretty row 6</li>
<li>Pretty row 7</li>
<li>Pretty row 8</li>
<li>
<input type="checkbox"></li>
<li>Pretty row 10</li>
<li>Pretty row 11</li>
<li>Pretty row 12</li>
<li>
<input type="radio"></li>
<li>Pretty row 14</li>
<li>Pretty row 15</li>
<li>Pretty row 16</li>
<li>
<textarea></textarea></li>
<li>Pretty row 18</li>
<li>Pretty row 19</li>
<li>Pretty row 20</li>
<li>
<select>
<option>option</option>
</select></li>
<li>Pretty row 22</li>
<li>Pretty row 23</li>
<li>Pretty row 24</li>
</ul>
<hr />
<ul>
<li>Pretty row 25</li>
<li>Pretty row 26</li>
<li>Pretty row 27</li>
<li>Pretty row 28</li>
<li>Pretty row 29</li>
<li>Pretty row 30</li>
<li>Pretty row 31</li>
<li>Pretty row 32</li>
<li>Pretty row 33</li>
<li>Pretty row 34</li>
<li>Pretty row 35</li>
<li>Pretty row 36</li>
<li>Pretty row 37</li>
<li>Pretty row 38</li>
<li>Pretty row 39</li>
<li>Pretty row 40</li>
<li>Pretty row 41</li>
<li>Pretty row 42</li>
<li>Pretty row 43</li>
<li>Pretty row 44</li>
<li>Pretty row 45</li>
<li>Pretty row 46</li>
<li>Pretty row 47</li>
<li>Pretty row 48</li>
<li>Pretty row 49</li>
<li>Pretty row 50</li>
</ul>
</div>
</div>
<footer>
<h1>
Footer</h1>
</footer>
<script src="../../zepto/zepto-1.0/src/zepto.js" type="text/javascript"></script>
<script type="text/javascript">
var Scroll = function (opts) {
opts = opts || {};
//检测设备事件支持,确定使用鼠标事件或者touch事件
this._checkEventCompatibility();
this._setBaseParam(opts);
this._initScrollBar();
// this._addEvent();
}; Scroll.prototype = {
constructor: Scroll,
//检测设备事件兼容
_checkEventCompatibility: function () {
var isTouch = 'ontouchstart' in document.documentElement;
this.start = isTouch ? 'touchstart' : 'mousedown';
this.move = isTouch ? 'touchmove' : 'mousemove';
this.end = isTouch ? 'touchend' : 'mouseup';
this.startFn;
this.moveFn;
this.endFn;
},
//基本参数设置
_setBaseParam: function (opts) {
this.timeGap = 0; //时间间隔
this.touchTime = 0; //开始时间
this.isMoveing = false; //是否正在移动
this.moveState = 'up'; //移动状态,up right down left
this.oTop = 0; //拖动前的top值
this.curTop = 0; //当前容器top
this.mouseY = 0; //鼠标第一次点下时相对父容器的位置
this.animateParam = opts.animateParam || [10, 8, 6, 5, 4, 3, 2, 1, 0, 0, 0]; //动画参数
this.cooling = true; //是否处于冷却时间
this.steplen = 25; //动画步长 this.wrapper = opts.wrapper || $('body');
this.dragEl = opts.body;
this.wrapper.css({ 'position': 'absolute', 'overflow': 'hidden' });
this.dragEl.css('position', 'absolute');
this.wrapper.append(this.dragEl);
},
_initScrollBar: function () {
if (!this.dragHeight) {
this.dragHeight = this.dragEl.offset().height; //拖动元素高度
this.wrapperHeight = this.wrapper.offset().height;
}
//滚动条缩放比例
this.scrollProportion = this.wrapperHeight / this.dragHeight;
this.isNeedScrollBar = true;
//该种情况无需滚动条
if (this.scrollProportion >= 1) {
this.isNeedScrollBar = false; ;
return false;
}
//滚动条
this.scrollBar = $('<div style="background-color: rgba(0, 0, 0, 0.498039);border: 1px solid rgba(255, 255, 255, 0.901961); width: 5px; border-radius: 3px; position: absolute; right: 1px; opacity: 0.2; "></div>');
this.wrapper.append(this.scrollBar);
this.scrollHeight = parseInt(this.scrollProportion * this.wrapperHeight);
this.scrollBar.css('height', this.scrollHeight);
} };
new Scroll({ wrapper: $('#body'), body: $('#wrapper') });
</script>
</body>
</html>

http://sandbox.runjs.cn/show/oztjkadg(最好使用手机访问)

事件绑定

样式出来了,我们现在就该注册事件了,支持touch就是要touch,否则就是要mouse事件了:

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<style type="text/css">
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td, hr, button, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { margin: 0; padding: 0; }
body { font: normal 14px/1.5 "Arial" , "Lucida Grande" ,Verdana, "Microsoft YaHei" , "hei"; -webkit-font-smoothing: antialiased; color: #000; background: #ccc; }
header { position: absolute; top: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; }
footer { position: absolute; bottom: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; }
h1 { display: block; font-size: 2em; font-weight: bold; font-weight: 500; text-align: center; color: White; } #body { position: absolute; top: 50px; bottom: 50px; background: #fff; width: 100%; overflow: hidden; }
#wrapper { width: 100%; } #wrapper li { padding: 0 10px; height: 40px; line-height: 40px; border-bottom: 1px solid #ccc; border-top: 1px solid #fff; background-color: #fafafa; font-size: 14px; }
</style>
<script id="others_zepto_10rc1" type="text/javascript" class="library" src="/js/sandbox/other/zepto.min.js"></script>
</head>
<body>
<header id="header">
<h1>
Header</h1>
</header>
<div id="body">
<div id="wrapper">
body
<ul>
<li>Pretty row 1</li>
<li>Pretty row 2</li>
<li>Pretty row 3</li>
<li>Pretty row 4</li>
<li>
<input type="text"></li>
<li>Pretty row 6</li>
<li>Pretty row 7</li>
<li>Pretty row 8</li>
<li>
<input type="checkbox"></li>
<li>Pretty row 10</li>
<li>Pretty row 11</li>
<li>Pretty row 12</li>
<li>
<input type="radio"></li>
<li>Pretty row 14</li>
<li>Pretty row 15</li>
<li>Pretty row 16</li>
<li>
<textarea></textarea></li>
<li>Pretty row 18</li>
<li>Pretty row 19</li>
<li>Pretty row 20</li>
<li>
<select>
<option>option</option>
</select></li>
</ul>
<hr />
<ul> <li>Pretty row 41</li>
<li>Pretty row 42</li>
<li>Pretty row 43</li>
<li>Pretty row 44</li>
<li>Pretty row 45</li>
<li>Pretty row 46</li>
<li>Pretty row 47</li>
<li>Pretty row 48</li>
<li>Pretty row 49</li>
<li>Pretty row 50</li>
</ul>
</div>
</div>
<footer>
<h1>
Footer</h1>
</footer>
<script src="zepto.js" type="text/javascript"></script>
<script type="text/javascript">
var Scroll = function (opts) {
opts = opts || {};
//检测设备事件支持,确定使用鼠标事件或者touch事件
this._checkEventCompatibility();
this._setBaseParam(opts);
this._addEvent(); this._initScrollBar();
}; Scroll.prototype = {
constructor: Scroll,
//检测设备事件兼容
_checkEventCompatibility: function () {
var isTouch = 'ontouchstart' in document.documentElement; this.start = isTouch ? 'touchstart' : 'mousedown';
this.move = isTouch ? 'touchmove' : 'mousemove';
this.end = isTouch ? 'touchend' : 'mouseup';
this.startFn;
this.moveFn;
this.endFn;
},
//基本参数设置
_setBaseParam: function (opts) {
this.timeGap = 0; //时间间隔
this.touchTime = 0; //开始时间
this.isMoveing = false; //是否正在移动
this.moveState = 'up'; //移动状态,up right down left
this.oTop = 0; //拖动前的top值
this.curTop = 0; //当前容器top
this.mouseY = 0; //鼠标第一次点下时相对父容器的位置
this.animateParam = opts.animateParam || [10, 8, 6, 5, 4, 3, 2, 1, 0, 0, 0]; //动画参数
this.cooling = true; //是否处于冷却时间
this.steplen = 25; //动画步长 this.wrapper = opts.wrapper || $('body');
this.dragEl = opts.body;
this.wrapper.css({ 'position': 'absolute', 'overflow': 'hidden' });
this.dragEl.css('position', 'absolute');
this.wrapper.append(this.dragEl);
},
_initScrollBar: function () {
if (!this.dragHeight) {
this.dragHeight = this.dragEl.offset().height; //拖动元素高度
this.wrapperHeight = this.wrapper.offset().height;
}
//滚动条缩放比例
this.scrollProportion = this.wrapperHeight / this.dragHeight;
this.isNeedScrollBar = true;
//该种情况无需滚动条
if (this.scrollProportion >= 1) {
this.isNeedScrollBar = false; ;
return false;
}
//滚动条
this.scrollBar = $('<div style="background-color: rgba(0, 0, 0, 0.498039);border: 1px solid rgba(255, 255, 255, 0.901961); width: 5px; border-radius: 3px; position: absolute; right: 1px; opacity: 0.2; "></div>');
this.wrapper.append(this.scrollBar);
this.scrollHeight = parseInt(this.scrollProportion * this.wrapperHeight);
this.scrollBar.css('height', this.scrollHeight);
},
_setScrollTop: function (top, duration) {
//滚动条高度
if (this.isNeedScrollBar) {
top = this._getResetData(top).sTop;
top = top < 0 ? (top + 10) : top; var scrollTop = top * (-1);
if (typeof duration == 'number') {
var _top = parseInt(scrollTop * this.scrollProportion) + 'px';
this.scrollBar.animate({
top: _top,
right: '1px'
}, duration, 'linear'); } else {
this.scrollBar.css('top', parseInt(scrollTop * this.scrollProportion) + 'px');
}
this.scrollBar.css('opacity', '0.8');
}
},
_hideScroll: function () {
if (this.isNeedScrollBar) {
this.scrollBar.animate({ 'opacity': '0.2' });
}
},
_addEvent: function () {
var scope = this;
this.startFn = function (e) {
scope._touchStart.call(scope, e);
};
this.moveFn = function (e) {
scope._touchMove.call(scope, e);
};
this.endFn = function (e) {
scope._touchEnd.call(scope, e);
};
this.dragEl[0].addEventListener(this.start, this.startFn, false);
document.addEventListener(this.move, this.moveFn, false);
document.addEventListener(this.end, this.endFn, false);
},
removeEvent: function () {
this.dragEl[0].removeEventListener(this.start, this.startFn);
document.removeEventListener(this.move, this.moveFn);
document.removeEventListener(this.end, this.endFn);
},
_touchStart: function (e) {
var scope = this;
if (this.isMoveing) { e.preventDefault(); return false; }
//非运动情况关闭冷却时间
this.cooling = false;
this.touchTime = e.timeStamp;
pos = this.getMousePos((e.changedTouches && e.changedTouches[0]) || e);
var top = parseFloat(this.dragEl.css('top')) || 0;
this.mouseY = pos.top - top;
},
_touchMove: function (e) {
if (this.cooling) { e.preventDefault(); return false; } this.isMoveing = true; e.preventDefault();
var pos = this.getMousePos((e.changedTouches && e.changedTouches[0]) || e); //防止点击时候跳动
if (Math.abs((pos.top - this.mouseY) - this.curTop) < 10) { e.preventDefault(); return false; } //先获取相对容器的位置,在将两个鼠标位置相减
this.curTop = pos.top - this.mouseY; var resetData = this._getResetData(this.curTop);
if (resetData.needReset) {
this.curTop = this._resetEdge(this.curTop);
} this.dragEl.css('top', this.curTop + 'px');
this._setScrollTop(this.curTop);
e.preventDefault(); },
_touchEnd: function (e) {
if (this.cooling) { e.preventDefault(); return false; }
if (Math.abs(this.oTop - this.curTop) < 10) { e.preventDefault(); return false; }
//一次动作结束,开启冷却时间
this.cooling = true;
var scope = this;
this.timeGap = e.timeStamp - this.touchTime;
var flag = this.oTop < this.curTop ? 1 : -1; //判断是向上还是向下滚动
this.moveState = flag > 0 ? 'up' : 'down'; var step = parseInt(this.timeGap / 10 - 10);
step = step > 0 ? step : 0;
var speed = this.animateParam[step] || 0;
var increment = speed * this.steplen * flag;
var top = this.curTop;
top += increment; var resetData = this._getResetData(top);
if (resetData.needReset) {
top = this._resetEdge(top);
speed = 0;
} //!!!此处动画可能导致数据不同步,后期改造需要加入冷却时间
if (this.oTop != this.curTop && this.curTop != top) {
var duration = 100 + (speed * 20);
top += increment;
this.dragEl.animate({
top: top + 'px'
}, duration, 'linear', function () {
scope.reset.call(scope, top); });
this._setScrollTop(top, duration);
} else {
this.isMoveing = false;
this.oTop = top;
this.reset(top);
this.cooling = false; //关闭冷却时间
}
this._hideScroll();
e.preventDefault();
},
_resetEdge: function (top) {
var h1 = parseInt(this.wrapperHeight / 3);
var h2 = parseInt(this.dragHeight * (-1) + this.wrapperHeight * (2 / 3));
if (top > 0 && top > h1) top = h1;
if (top < 0 && top < h2) top = h2;
return top;
},
_getResetData: function (top) {
var needReset = false;
var sTop = top;
if (top < (-1) * (this.dragHeight - this.wrapperHeight)) { top = (-1) * (this.dragHeight - this.wrapperHeight); needReset = true; }
if (top > 0) { top = 0; needReset = true; } return {
top: top,
sTop: sTop,
needReset: needReset
};
},
//超出限制后位置还原
reset: function (top) {
var scope = this;
var needReset = this._getResetData(top).needReset;
var top = this._getResetData(top).top; if (needReset) {
scope.dragEl.animate({
top: top + 'px'
}, 50, 'linear', function () {
scope._reset(top);
scope._setScrollTop(top); });
} else {
scope._reset(top);
}
},
_reset: function (top) {
this.oTop = top;
this.curTop = top;
this.isMoveing = false;
this.cooling = false; //关闭冷却时间
},
//获取鼠标信息
getMousePos: function (event) {
var top, left;
top = Math.max(document.body.scrollTop, document.documentElement.scrollTop);
left = Math.max(document.body.scrollLeft, document.documentElement.scrollLeft);
return {
top: top + event.clientY,
left: left + event.clientX
};
}
};
new Scroll({ wrapper: $('#body'), body: $('#wrapper') });
</script>
</body>
</html>

这个是我们第一步形成的代码,他具有以下问题待解决:

① 由于是CSS3实现的动画,不能保存状态,所以我们再次点击时候不能停止动画

② 未使用CSS3的transform,所以整个功能暂不支持3D加速

③ 由于touch时候的e.preventDefault,所以其中的文本框等在手机上不能获取焦点

以上焦点便是我们接下来需要解决的地方,上面提出了三大问题,我们这里来一一解决

硬件加速

各位看到上面实现动画的方法是通过变化元素的Top实现的,这样做原来有一个好处就是可以向下兼容,但是对于移动端来说意义不大

事实上这里的top实现动画变为translate实现动画更为舒服,原因是手机对CSS3动画做了处理,可以开启硬件加速

我们可以在浏览器中用css开启硬件加速,使GPU (Graphics Processing Unit) 发挥功能,从而提升性能

CSS animations, transforms 以及 transitions 不会自动开启GPU加速,而是由浏览器的缓慢的软件渲染引擎来执行。

.cube {
-webkit-transform: translate3d(250px,250px,250px)
rotate3d(250px,250px,250px,-120deg)
scale3d(0.5, 0.5, 0.5);
}

以上代码便会开启硬件加速,所以我们这里的对应关系是这样的:

top=>translate3d(0, 0, 0)

加速是好的,滥用可能引起性能问题,而且ios下动画可能产生抖动现象,这个各位一定要注意,于是通过这个,我们修改我们的代码:

http://sandbox.runjs.cn/show/wqw1lpcl(请用webkit手机对比)

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<style type="text/css">
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td, hr, button, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { margin: 0; padding: 0; }
body { font: normal 14px/1.5 "Arial" , "Lucida Grande" ,Verdana, "Microsoft YaHei" , "hei"; -webkit-font-smoothing: antialiased; color: #000; background: #ccc; }
header { position: absolute; top: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; }
footer { position: absolute; bottom: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; }
h1 { display: block; font-size: 2em; font-weight: bold; font-weight: 500; text-align: center; color: White; } #body { position: absolute; top: 50px; bottom: 50px; background: #fff; width: 100%; overflow: hidden; }
#wrapper { width: 100%; } #wrapper li { padding: 0 10px; height: 40px; line-height: 40px; border-bottom: 1px solid #ccc; border-top: 1px solid #fff; background-color: #fafafa; font-size: 14px; }
</style>
<script id="others_zepto_10rc1" type="text/javascript" class="library" src="/js/sandbox/other/zepto.min.js"></script>
</head>
<body>
<header id="header">
<h1>
Header</h1>
</header>
<div id="body">
<div id="wrapper">
body
<ul>
<li>Pretty row 1</li>
<li>Pretty row 2</li>
<li>Pretty row 3</li>
<li>Pretty row 4</li>
<li>
<input type="text"></li>
<li>Pretty row 6</li>
<li>Pretty row 7</li>
<li>Pretty row 8</li>
<li>
<input type="checkbox"></li>
<li>Pretty row 10</li>
<li>Pretty row 11</li>
<li>Pretty row 12</li>
<li>
<input type="radio"></li>
<li>Pretty row 14</li>
<li>Pretty row 15</li>
<li>Pretty row 16</li>
<li>
<textarea></textarea></li>
<li>Pretty row 18</li>
<li>Pretty row 19</li>
<li>Pretty row 20</li>
<li>
<select>
<option>option</option>
</select></li>
</ul>
<hr />
<ul> <li>Pretty row 41</li>
<li>Pretty row 42</li>
<li>Pretty row 43</li>
<li>Pretty row 44</li>
<li>Pretty row 45</li>
<li>Pretty row 46</li>
<li>Pretty row 47</li>
<li>Pretty row 48</li>
<li>Pretty row 49</li>
<li>Pretty row 50</li>
</ul>
</div>
</div>
<footer>
<h1>
Footer</h1>
</footer>
<script src="zepto.js" type="text/javascript"></script>
<script type="text/javascript">
var Scroll = function (opts) {
opts = opts || {};
//检测设备事件支持,确定使用鼠标事件或者touch事件
this._checkEventCompatibility();
this._setBaseParam(opts);
this._addEvent(); this._initScrollBar();
}; Scroll.prototype = {
constructor: Scroll,
//检测设备事件兼容
_checkEventCompatibility: function () {
var isTouch = 'ontouchstart' in document.documentElement;
isTouch = true; this.start = isTouch ? 'touchstart' : 'mousedown';
this.move = isTouch ? 'touchmove' : 'mousemove';
this.end = isTouch ? 'touchend' : 'mouseup';
this.startFn;
this.moveFn;
this.endFn;
},
//基本参数设置
_setBaseParam: function (opts) {
this.timeGap = 0; //时间间隔
this.touchTime = 0; //开始时间
this.isMoveing = false; //是否正在移动
this.moveState = 'up'; //移动状态,up right down left
this.oTop = 0; //拖动前的top值
this.curTop = 0; //当前容器top
this.mouseY = 0; //鼠标第一次点下时相对父容器的位置
this.animateParam = opts.animateParam || [10, 8, 6, 5, 4, 3, 2, 1, 0, 0, 0]; //动画参数
this.cooling = true; //是否处于冷却时间
this.steplen = 25; //动画步长 this.wrapper = opts.wrapper || $('body');
this.dragEl = opts.body;
this.wrapper.css({ 'position': 'absolute', 'overflow': 'hidden' });
this.dragEl.css('position', 'absolute');
this.wrapper.append(this.dragEl);
},
_initScrollBar: function () {
if (!this.dragHeight) {
this.dragHeight = this.dragEl.offset().height; //拖动元素高度
this.wrapperHeight = this.wrapper.offset().height;
}
//滚动条缩放比例
this.scrollProportion = this.wrapperHeight / this.dragHeight;
this.isNeedScrollBar = true;
//该种情况无需滚动条
if (this.scrollProportion >= 1) {
this.isNeedScrollBar = false; ;
return false;
}
//滚动条
this.scrollBar = $('<div style="background-color: rgba(0, 0, 0, 0.498039);border: 1px solid rgba(255, 255, 255, 0.901961); width: 5px; border-radius: 3px; position: absolute; right: 1px; opacity: 0.2; "></div>');
this.wrapper.append(this.scrollBar);
this.scrollHeight = parseInt(this.scrollProportion * this.wrapperHeight);
this.scrollBar.css('height', this.scrollHeight);
},
_setScrollTop: function (top, duration) {
//滚动条高度
if (this.isNeedScrollBar) {
top = this._getResetData(top).sTop;
top = top < 0 ? (top + 10) : top; var scrollTop = top * (-1);
if (typeof duration == 'number') {
var _top = parseInt(scrollTop * this.scrollProportion) + 'px';
this.scrollBar.animate({
'-webkit-transform': 'translate3d(0, ' + _top + ', 0)'
}, duration, 'linear'); } else {
var _st = parseInt(scrollTop * this.scrollProportion)
this.scrollBar.css('-webkit-transform', 'translate3d(0, ' + _st + 'px, 0)');
}
this.scrollBar.css('opacity', '0.8');
}
},
_hideScroll: function () {
if (this.isNeedScrollBar) {
this.scrollBar.css({ 'opacity': '0.2' });
}
},
_addEvent: function () {
var scope = this;
this.startFn = function (e) {
scope._touchStart.call(scope, e);
};
this.moveFn = function (e) {
scope._touchMove.call(scope, e);
};
this.endFn = function (e) {
scope._touchEnd.call(scope, e);
};
this.dragEl[0].addEventListener(this.start, this.startFn, false);
document.addEventListener(this.move, this.moveFn, false);
document.addEventListener(this.end, this.endFn, false);
},
removeEvent: function () {
this.dragEl[0].removeEventListener(this.start, this.startFn);
document.removeEventListener(this.move, this.moveFn);
document.removeEventListener(this.end, this.endFn);
},
_touchStart: function (e) {
var scope = this;
if (this.isMoveing) { e.preventDefault(); return false; }
//非运动情况关闭冷却时间
this.cooling = false;
this.touchTime = e.timeStamp;
pos = this.getMousePos((e.changedTouches && e.changedTouches[0]) || e);
// var top = parseFloat(this.dragEl.css('top')) || 0;
var top = this._cssTranslate(this.dragEl);
this.mouseY = pos.top - top;
},
_touchMove: function (e) {
if (this.cooling) { e.preventDefault(); return false; } this.isMoveing = true; var pos = this.getMousePos((e.changedTouches && e.changedTouches[0]) || e); //防止点击时候跳动
if (Math.abs((pos.top - this.mouseY) - this.curTop) < 10) { e.preventDefault(); return false; } //先获取相对容器的位置,在将两个鼠标位置相减
this.curTop = pos.top - this.mouseY; var resetData = this._getResetData(this.curTop);
if (resetData.needReset) {
this.curTop = this._resetEdge(this.curTop);
} // this.dragEl.css('top', this.curTop + 'px');
this._cssTranslate(this.dragEl, this.curTop); this._setScrollTop(this.curTop);
e.preventDefault(); },
_touchEnd: function (e) {
if (this.cooling) { e.preventDefault(); return false; }
if (Math.abs(this.oTop - this.curTop) < 10) { e.preventDefault(); return false; }
//一次动作结束,开启冷却时间
this.cooling = true;
var scope = this;
this.timeGap = e.timeStamp - this.touchTime;
var flag = this.oTop < this.curTop ? 1 : -1; //判断是向上还是向下滚动
this.moveState = flag > 0 ? 'up' : 'down'; var step = parseInt(this.timeGap / 10 - 10);
step = step > 0 ? step : 0;
var speed = this.animateParam[step] || 0;
var increment = speed * this.steplen * flag;
var top = this.curTop;
top += increment; var resetData = this._getResetData(top);
if (resetData.needReset) {
top = this._resetEdge(top);
speed = 0;
} //!!!此处动画可能导致数据不同步,后期改造需要加入冷却时间
if (this.oTop != this.curTop && this.curTop != top) {
var duration = 100 + (speed * 20);
top += increment;
this.dragEl.animate({
'-webkit-transform': 'translate3d(0, ' + top + 'px, 0)'
}, duration, 'linear', function () {
scope.reset.call(scope, top); });
this._setScrollTop(top, duration);
} else {
this.isMoveing = false;
this.oTop = top;
this.reset(top);
this.cooling = false; //关闭冷却时间
}
this._hideScroll();
e.preventDefault();
},
_resetEdge: function (top) {
var h1 = parseInt(this.wrapperHeight / 3);
var h2 = parseInt(this.dragHeight * (-1) + this.wrapperHeight * (2 / 3));
if (top > 0 && top > h1) top = h1;
if (top < 0 && top < h2) top = h2;
return top;
},
_getResetData: function (top) {
var needReset = false;
var sTop = top;
if (top < (-1) * (this.dragHeight - this.wrapperHeight)) { top = (-1) * (this.dragHeight - this.wrapperHeight); needReset = true; }
if (top > 0) { top = 0; needReset = true; } return {
top: top,
sTop: sTop,
needReset: needReset
};
},
//超出限制后位置还原
reset: function (top) {
var scope = this;
var needReset = this._getResetData(top).needReset;
var top = this._getResetData(top).top; if (needReset) {
scope.dragEl.animate({
'-webkit-transform': 'translate3d(0, ' + top + 'px, 0)' }, 50, 'linear', function () {
scope._reset(top);
scope._setScrollTop(top); });
} else {
scope._reset(top);
}
},
_reset: function (top) {
this.oTop = top;
this.curTop = top;
this.isMoveing = false;
this.cooling = false; //关闭冷却时间
},
//暂时仅用于,操作Y值
_cssTranslate: function (el, y) {
if (!el) return 0;
if (typeof y == 'number') {
el.css('-webkit-transform', 'translate3d(0, ' + y + 'px, 0)');
}
var data = /\((.*)\)/.exec(el.css('-webkit-transform'));
if (data && typeof data[1] == 'string') data = data[1].split(',');
if (data && typeof data[1] == 'string') return parseInt(data[1]);
return 0;
},
//获取鼠标信息
getMousePos: function (event) {
var top, left;
top = Math.max(document.body.scrollTop, document.documentElement.scrollTop);
left = Math.max(document.body.scrollLeft, document.documentElement.scrollLeft);
return {
top: top + event.clientY,
left: left + event.clientX
};
}
};
new Scroll({ wrapper: $('#body'), body: $('#wrapper') });
</script>
</body>
</html>

PS:对比下来,我想说,硬件加速的感觉真他妈爽!!!!这段代码没有过多测试,有问题请留言

停止CSS动画

要停止CSS动画,并且要保存CSS的状态,这个问题其实在三个问题中,我认为是最难的,因为我们可能遇到如下需求:

① 移动过程手指触摸屏幕,动画停止

② 连续滑动时候需要动画加速

如何停止CSS3的动画?

我这里自然处理不到这么复杂的问题,所以就先实现停止动画即可

<div id="wrapper" style="position: absolute; -webkit-transform: translate3d(0px, -558px, 0px);
-webkit-transition: -webkit-transform 20.1s linear; transition: -webkit-transform 20.1s linear;">
</div>

这个就是zepto一次动画获得的参数,我故意将时间设置的很长,我们要在点击时候马上获取transform,并且重新设置

http://sandbox.runjs.cn/show/vgekfj8f

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<style type="text/css">
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td, hr, button, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { margin: 0; padding: 0; }
body { font: normal 14px/1.5 "Arial" , "Lucida Grande" ,Verdana, "Microsoft YaHei" , "hei"; -webkit-font-smoothing: antialiased; color: #000; background: #ccc; }
header { position: absolute; top: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; }
footer { position: absolute; bottom: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; }
h1 { display: block; font-size: 2em; font-weight: bold; font-weight: 500; text-align: center; color: White; } #body { position: absolute; top: 50px; bottom: 50px; background: #fff; width: 100%; overflow: hidden; }
#wrapper { width: 100%; } #wrapper li { padding: 0 10px; height: 40px; line-height: 40px; border-bottom: 1px solid #ccc; border-top: 1px solid #fff; background-color: #fafafa; font-size: 14px; }
</style>
<script id="others_zepto_10rc1" type="text/javascript" class="library" src="/js/sandbox/other/zepto.min.js"></script>
</head>
<body>
<header id="header">
<h1>
Header</h1>
</header>
<div id="body">
<div id="wrapper">
body
<ul>
<li>Pretty row 1</li>
<li>Pretty row 2</li>
<li>Pretty row 3</li>
<li>Pretty row 4</li>
<li>
<input type="text"></li>
<li>Pretty row 6</li>
<li>Pretty row 7</li>
<li>Pretty row 8</li>
<li>
<input type="checkbox"></li>
<li>Pretty row 10</li>
<li>Pretty row 11</li>
<li>Pretty row 12</li>
<li>
<input type="radio"></li>
<li>Pretty row 14</li>
<li>Pretty row 15</li>
<li>Pretty row 16</li>
<li>
<textarea></textarea></li>
<li>Pretty row 18</li>
<li>Pretty row 19</li>
<li>Pretty row 20</li>
<li>
<select>
<option>option</option>
</select></li>
</ul>
<hr />
<ul> <li>Pretty row 41</li>
<li>Pretty row 42</li>
<li>Pretty row 43</li>
<li>Pretty row 44</li>
<li>Pretty row 45</li>
<li>Pretty row 46</li>
<li>Pretty row 47</li>
<li>Pretty row 48</li>
<li>Pretty row 49</li>
<li>Pretty row 50</li>
</ul>
</div>
</div>
<footer>
<h1>
Footer</h1>
</footer>
<script src="zepto.js" type="text/javascript"></script>
<script type="text/javascript">
var Scroll = function (opts) {
opts = opts || {};
//检测设备事件支持,确定使用鼠标事件或者touch事件
this._checkEventCompatibility();
this._setBaseParam(opts);
this._addEvent(); this._initScrollBar();
}; Scroll.prototype = {
constructor: Scroll,
//检测设备事件兼容
_checkEventCompatibility: function () {
var isTouch = 'ontouchstart' in document.documentElement;
// isTouch = true; this.start = isTouch ? 'touchstart' : 'mousedown';
this.move = isTouch ? 'touchmove' : 'mousemove';
this.end = isTouch ? 'touchend' : 'mouseup';
this.startFn;
this.moveFn;
this.endFn;
},
//基本参数设置
_setBaseParam: function (opts) {
this.timeGap = 0; //时间间隔
this.touchTime = 0; //开始时间
this.isMoveing = false; //是否正在移动
this.moveState = 'up'; //移动状态,up right down left
this.oTop = 0; //拖动前的top值
this.curTop = 0; //当前容器top
this.mouseY = 0; //鼠标第一次点下时相对父容器的位置
this.animateParam = opts.animateParam || [10, 8, 6, 5, 4, 3, 2, 1, 0, 0, 0]; //动画参数
this.cooling = true; //是否处于冷却时间
this.steplen = 25; //动画步长 this.wrapper = opts.wrapper || $('body');
this.dragEl = opts.body;
this.wrapper.css({ 'position': 'absolute', 'overflow': 'hidden' });
this.dragEl.css('position', 'absolute');
this.wrapper.append(this.dragEl);
},
_initScrollBar: function () {
if (!this.dragHeight) {
this.dragHeight = this.dragEl.offset().height; //拖动元素高度
this.wrapperHeight = this.wrapper.offset().height;
}
//滚动条缩放比例
this.scrollProportion = this.wrapperHeight / this.dragHeight;
this.isNeedScrollBar = true;
//该种情况无需滚动条
if (this.scrollProportion >= 1) {
this.isNeedScrollBar = false; ;
return false;
}
//滚动条
this.scrollBar = $('<div style="background-color: rgba(0, 0, 0, 0.498039);border: 1px solid rgba(255, 255, 255, 0.901961); width: 5px; border-radius: 3px; position: absolute; right: 1px; opacity: 0.2; "></div>');
this.wrapper.append(this.scrollBar);
this.scrollHeight = parseInt(this.scrollProportion * this.wrapperHeight);
this.scrollBar.css('height', this.scrollHeight);
},
_setScrollTop: function (top, duration) {
//滚动条高度
if (this.isNeedScrollBar) {
top = this._getResetData(top).sTop;
top = top < 0 ? (top + 10) : top; var scrollTop = top * (-1);
if (typeof duration == 'number') {
var _top = parseInt(scrollTop * this.scrollProportion) + 'px';
this.scrollBar.animate({
'-webkit-transform': 'translate3d(0, ' + _top + ', 0)'
}, duration, 'linear'); } else {
var _st = parseInt(scrollTop * this.scrollProportion)
this.scrollBar.css('-webkit-transform', 'translate3d(0, ' + _st + 'px, 0)');
}
this.scrollBar.css('opacity', '0.8');
}
},
_hideScroll: function () {
if (this.isNeedScrollBar) {
this.scrollBar.css({ 'opacity': '0.2' });
}
},
_addEvent: function () {
var scope = this;
this.startFn = function (e) {
scope._touchStart.call(scope, e);
};
this.moveFn = function (e) {
scope._touchMove.call(scope, e);
};
this.endFn = function (e) {
scope._touchEnd.call(scope, e);
};
this.dragEl[0].addEventListener(this.start, this.startFn, false);
document.addEventListener(this.move, this.moveFn, false);
document.addEventListener(this.end, this.endFn, false);
},
removeEvent: function () {
this.dragEl[0].removeEventListener(this.start, this.startFn);
document.removeEventListener(this.move, this.moveFn);
document.removeEventListener(this.end, this.endFn);
},
_touchStart: function (e) {
var scope = this;
window.dragEl = this.dragEl; if (this.isMoveing) { var el = this.dragEl[0];
var computedStyle = document.defaultView.getComputedStyle(el, null);
// computedStyle.getPropertyValue("width"); var top = 0;
var data = /\((.*)\)/.exec(computedStyle.getPropertyValue("-webkit-transform"));
if (typeof data == 'object') data = data[1].split(',');
if (typeof data == 'object') top = parseInt(data[5]);
console.log(top); this.dragEl.css('-webkit-transition', '-webkit-transform 0s linear');
this.dragEl.css('transition', '-webkit-transform 0s linear'); this._cssTranslate(this.dragEl, top);
this._setScrollTop(top); this.isMoveing = false;
e.preventDefault(); return false;
} this.clickEl = e.target; //非运动情况关闭冷却时间
this.cooling = false;
this.touchTime = e.timeStamp;
pos = this.getMousePos((e.changedTouches && e.changedTouches[0]) || e);
// var top = parseFloat(this.dragEl.css('top')) || 0;
var top = this._cssTranslate(this.dragEl);
this.mouseY = pos.top - top;
},
_touchMove: function (e) {
if (this.cooling) { e.preventDefault(); return false; } this.isMoveing = true; var pos = this.getMousePos((e.changedTouches && e.changedTouches[0]) || e); //防止点击时候跳动
if (Math.abs((pos.top - this.mouseY) - this.curTop) < 10) { e.preventDefault(); return false; } //先获取相对容器的位置,在将两个鼠标位置相减
this.curTop = pos.top - this.mouseY; var resetData = this._getResetData(this.curTop);
if (resetData.needReset) {
this.curTop = this._resetEdge(this.curTop);
} // this.dragEl.css('top', this.curTop + 'px');
this._cssTranslate(this.dragEl, this.curTop); this._setScrollTop(this.curTop);
e.preventDefault(); },
_touchEnd: function (e) { if (this._needFocus(this.clickEl)) {
$(this.clickEl).focus();
return;
} if (this.cooling) { e.preventDefault(); return false; }
if (Math.abs(this.oTop - this.curTop) < 10) { e.preventDefault(); return false; }
//一次动作结束,开启冷却时间
this.cooling = true;
var scope = this;
this.timeGap = e.timeStamp - this.touchTime;
var flag = this.oTop < this.curTop ? 1 : -1; //判断是向上还是向下滚动
this.moveState = flag > 0 ? 'up' : 'down'; var step = parseInt(this.timeGap / 10 - 10);
step = step > 0 ? step : 0;
var speed = this.animateParam[step] || 0;
var increment = speed * this.steplen * flag;
var top = this.curTop;
top += increment; var resetData = this._getResetData(top);
if (resetData.needReset) {
top = this._resetEdge(top);
speed = 0;
} speed = 1000; //!!!此处动画可能导致数据不同步,后期改造需要加入冷却时间
if (this.oTop != this.curTop && this.curTop != top) {
var duration = 100 + (speed * 20);
top += increment;
this.dragEl.animate({
'-webkit-transform': 'translate3d(0, ' + top + 'px, 0)'
}, duration, 'linear', function () {
scope.reset.call(scope, top); });
this._setScrollTop(top, duration);
} else {
this.isMoveing = false;
this.oTop = top;
this.reset(top);
this.cooling = false; //关闭冷却时间
}
this._hideScroll();
e.preventDefault();
},
_resetEdge: function (top) {
var h1 = parseInt(this.wrapperHeight / 3);
var h2 = parseInt(this.dragHeight * (-1) + this.wrapperHeight * (2 / 3));
if (top > 0 && top > h1) top = h1;
if (top < 0 && top < h2) top = h2;
return top;
},
_getResetData: function (top) {
var needReset = false;
var sTop = top;
if (top < (-1) * (this.dragHeight - this.wrapperHeight)) { top = (-1) * (this.dragHeight - this.wrapperHeight); needReset = true; }
if (top > 0) { top = 0; needReset = true; } return {
top: top,
sTop: sTop,
needReset: needReset
};
},
//超出限制后位置还原
reset: function (top) {
var scope = this;
var needReset = this._getResetData(top).needReset;
var top = this._getResetData(top).top; if (needReset) {
scope.dragEl.animate({
'-webkit-transform': 'translate3d(0, ' + top + 'px, 0)' }, 50, 'linear', function () {
scope._reset(top);
scope._setScrollTop(top); });
} else {
scope._reset(top);
}
},
_reset: function (top) {
this.oTop = top;
this.curTop = top;
this.isMoveing = false;
this.cooling = false; //关闭冷却时间
},
//暂时仅用于,操作Y值
_cssTranslate: function (el, y) {
if (!el) return 0;
if (typeof y == 'number') {
el.css('-webkit-transform', 'translate3d(0, ' + y + 'px, 0)');
}
var data = /\((.*)\)/.exec(el.css('-webkit-transform'));
if (data && typeof data[1] == 'string') data = data[1].split(',');
if (data && typeof data[1] == 'string') return parseInt(data[1]);
return 0;
},
_needFocus: function (el) {
switch (el.nodeName.toLowerCase()) {
case 'textarea':
case 'select':
return true;
case 'input':
switch (el.type) {
case 'button':
case 'checkbox':
case 'file':
case 'image':
case 'radio':
case 'submit':
return false;
}
return !el.disabled && !el.readOnly;
default:
return (/\bneedfocus\b/).test(el.className);
}
},
//获取鼠标信息
getMousePos: function (event) {
var top, left;
top = Math.max(document.body.scrollTop, document.documentElement.scrollTop);
left = Math.max(document.body.scrollLeft, document.documentElement.scrollLeft);
return {
top: top + event.clientY,
left: left + event.clientX
};
}
};
new Scroll({ wrapper: $('#body'), body: $('#wrapper') });
</script>
</body>
</html>

PS:时间比较晚了,代码未做检测,各位包含

文本获取焦点

由于e.preventDefault的效果,所以我们里面的按钮点击一键文本框获取焦点有点不好使,我这里主要解决文本获取焦点即可

我们在touchstart时候可以获取e.target,在touchend时候若是判断是一次点击事件并且target为文本框的话,便获取焦点。

PS:我这里因为暂时不用作生产,先简单实现即可

http://sandbox.runjs.cn/show/djkrwwno

 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<style type="text/css">
body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td, hr, button, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { margin: 0; padding: 0; }
body { font: normal 14px/1.5 "Arial" , "Lucida Grande" ,Verdana, "Microsoft YaHei" , "hei"; -webkit-font-smoothing: antialiased; color: #000; background: #ccc; }
header { position: absolute; top: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; }
footer { position: absolute; bottom: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; }
h1 { display: block; font-size: 2em; font-weight: bold; font-weight: 500; text-align: center; color: White; } #body { position: absolute; top: 50px; bottom: 50px; background: #fff; width: 100%; overflow: hidden; }
#wrapper { width: 100%; } #wrapper li { padding: 0 10px; height: 40px; line-height: 40px; border-bottom: 1px solid #ccc; border-top: 1px solid #fff; background-color: #fafafa; font-size: 14px; }
</style>
<script id="others_zepto_10rc1" type="text/javascript" class="library" src="/js/sandbox/other/zepto.min.js"></script>
</head>
<body>
<header id="header">
<h1>
Header</h1>
</header>
<div id="body">
<div id="wrapper">
body
<ul>
<li>Pretty row 1</li>
<li>Pretty row 2</li>
<li>Pretty row 3</li>
<li>Pretty row 4</li>
<li>
<input type="text"></li>
<li>Pretty row 6</li>
<li>Pretty row 7</li>
<li>Pretty row 8</li>
<li>
<input type="checkbox"></li>
<li>Pretty row 10</li>
<li>Pretty row 11</li>
<li>Pretty row 12</li>
<li>
<input type="radio"></li>
<li>Pretty row 14</li>
<li>Pretty row 15</li>
<li>Pretty row 16</li>
<li>
<textarea></textarea></li>
<li>Pretty row 18</li>
<li>Pretty row 19</li>
<li>Pretty row 20</li>
<li>
<select>
<option>option</option>
</select></li>
</ul>
<hr />
<ul> <li>Pretty row 41</li>
<li>Pretty row 42</li>
<li>Pretty row 43</li>
<li>Pretty row 44</li>
<li>Pretty row 45</li>
<li>Pretty row 46</li>
<li>Pretty row 47</li>
<li>Pretty row 48</li>
<li>Pretty row 49</li>
<li>Pretty row 50</li>
</ul>
</div>
</div>
<footer>
<h1>
Footer</h1>
</footer>
<script src="zepto.js" type="text/javascript"></script>
<script type="text/javascript">
var Scroll = function (opts) {
opts = opts || {};
//检测设备事件支持,确定使用鼠标事件或者touch事件
this._checkEventCompatibility();
this._setBaseParam(opts);
this._addEvent(); this._initScrollBar();
}; Scroll.prototype = {
constructor: Scroll,
//检测设备事件兼容
_checkEventCompatibility: function () {
var isTouch = 'ontouchstart' in document.documentElement; this.start = isTouch ? 'touchstart' : 'mousedown';
this.move = isTouch ? 'touchmove' : 'mousemove';
this.end = isTouch ? 'touchend' : 'mouseup';
this.startFn;
this.moveFn;
this.endFn;
},
//基本参数设置
_setBaseParam: function (opts) {
this.timeGap = 0; //时间间隔
this.touchTime = 0; //开始时间
this.isMoveing = false; //是否正在移动
this.moveState = 'up'; //移动状态,up right down left
this.oTop = 0; //拖动前的top值
this.curTop = 0; //当前容器top
this.mouseY = 0; //鼠标第一次点下时相对父容器的位置
this.animateParam = opts.animateParam || [10, 8, 6, 5, 4, 3, 2, 1, 0, 0, 0]; //动画参数
this.cooling = true; //是否处于冷却时间
this.steplen = 25; //动画步长 this.wrapper = opts.wrapper || $('body');
this.dragEl = opts.body;
this.wrapper.css({ 'position': 'absolute', 'overflow': 'hidden' });
this.dragEl.css('position', 'absolute');
this.wrapper.append(this.dragEl);
},
_initScrollBar: function () {
if (!this.dragHeight) {
this.dragHeight = this.dragEl.offset().height; //拖动元素高度
this.wrapperHeight = this.wrapper.offset().height;
}
//滚动条缩放比例
this.scrollProportion = this.wrapperHeight / this.dragHeight;
this.isNeedScrollBar = true;
//该种情况无需滚动条
if (this.scrollProportion >= 1) {
this.isNeedScrollBar = false; ;
return false;
}
//滚动条
this.scrollBar = $('<div style="background-color: rgba(0, 0, 0, 0.498039);border: 1px solid rgba(255, 255, 255, 0.901961); width: 5px; border-radius: 3px; position: absolute; right: 1px; opacity: 0.2; "></div>');
this.wrapper.append(this.scrollBar);
this.scrollHeight = parseInt(this.scrollProportion * this.wrapperHeight);
this.scrollBar.css('height', this.scrollHeight);
},
_setScrollTop: function (top, duration) {
//滚动条高度
if (this.isNeedScrollBar) {
top = this._getResetData(top).sTop;
top = top < 0 ? (top + 10) : top; var scrollTop = top * (-1);
if (typeof duration == 'number') {
var _top = parseInt(scrollTop * this.scrollProportion) + 'px';
this.scrollBar.animate({
'-webkit-transform': 'translate3d(0, ' + _top + ', 0)'
}, duration, 'linear'); } else {
var _st = parseInt(scrollTop * this.scrollProportion)
this.scrollBar.css('-webkit-transform', 'translate3d(0, ' + _st + 'px, 0)');
}
this.scrollBar.css('opacity', '0.8');
}
},
_hideScroll: function () {
if (this.isNeedScrollBar) {
this.scrollBar.css({ 'opacity': '0.2' });
}
},
_addEvent: function () {
var scope = this;
this.startFn = function (e) {
scope._touchStart.call(scope, e);
};
this.moveFn = function (e) {
scope._touchMove.call(scope, e);
};
this.endFn = function (e) {
scope._touchEnd.call(scope, e);
};
this.dragEl[0].addEventListener(this.start, this.startFn, false);
document.addEventListener(this.move, this.moveFn, false);
document.addEventListener(this.end, this.endFn, false);
},
removeEvent: function () {
this.dragEl[0].removeEventListener(this.start, this.startFn);
document.removeEventListener(this.move, this.moveFn);
document.removeEventListener(this.end, this.endFn);
},
_touchStart: function (e) {
var scope = this;
if (this.isMoveing) { e.preventDefault(); return false; } this.clickEl = e.target; //非运动情况关闭冷却时间
this.cooling = false;
this.touchTime = e.timeStamp;
pos = this.getMousePos((e.changedTouches && e.changedTouches[0]) || e);
// var top = parseFloat(this.dragEl.css('top')) || 0;
var top = this._cssTranslate(this.dragEl);
this.mouseY = pos.top - top;
},
_touchMove: function (e) {
if (this.cooling) { e.preventDefault(); return false; } this.isMoveing = true; var pos = this.getMousePos((e.changedTouches && e.changedTouches[0]) || e); //防止点击时候跳动
if (Math.abs((pos.top - this.mouseY) - this.curTop) < 10) { e.preventDefault(); return false; } //先获取相对容器的位置,在将两个鼠标位置相减
this.curTop = pos.top - this.mouseY; var resetData = this._getResetData(this.curTop);
if (resetData.needReset) {
this.curTop = this._resetEdge(this.curTop);
} // this.dragEl.css('top', this.curTop + 'px');
this._cssTranslate(this.dragEl, this.curTop); this._setScrollTop(this.curTop);
e.preventDefault(); },
_touchEnd: function (e) { if (this._needFocus(this.clickEl)) {
$(this.clickEl).focus();
return;
} if (this.cooling) { e.preventDefault(); return false; }
if (Math.abs(this.oTop - this.curTop) < 10) { e.preventDefault(); return false; }
//一次动作结束,开启冷却时间
this.cooling = true;
var scope = this;
this.timeGap = e.timeStamp - this.touchTime;
var flag = this.oTop < this.curTop ? 1 : -1; //判断是向上还是向下滚动
this.moveState = flag > 0 ? 'up' : 'down'; var step = parseInt(this.timeGap / 10 - 10);
step = step > 0 ? step : 0;
var speed = this.animateParam[step] || 0;
var increment = speed * this.steplen * flag;
var top = this.curTop;
top += increment; var resetData = this._getResetData(top);
if (resetData.needReset) {
top = this._resetEdge(top);
speed = 0;
} //!!!此处动画可能导致数据不同步,后期改造需要加入冷却时间
if (this.oTop != this.curTop && this.curTop != top) {
var duration = 100 + (speed * 20);
top += increment;
this.dragEl.animate({
'-webkit-transform': 'translate3d(0, ' + top + 'px, 0)'
}, duration, 'linear', function () {
scope.reset.call(scope, top); });
this._setScrollTop(top, duration);
} else {
this.isMoveing = false;
this.oTop = top;
this.reset(top);
this.cooling = false; //关闭冷却时间
}
this._hideScroll();
e.preventDefault();
},
_resetEdge: function (top) {
var h1 = parseInt(this.wrapperHeight / 3);
var h2 = parseInt(this.dragHeight * (-1) + this.wrapperHeight * (2 / 3));
if (top > 0 && top > h1) top = h1;
if (top < 0 && top < h2) top = h2;
return top;
},
_getResetData: function (top) {
var needReset = false;
var sTop = top;
if (top < (-1) * (this.dragHeight - this.wrapperHeight)) { top = (-1) * (this.dragHeight - this.wrapperHeight); needReset = true; }
if (top > 0) { top = 0; needReset = true; } return {
top: top,
sTop: sTop,
needReset: needReset
};
},
//超出限制后位置还原
reset: function (top) {
var scope = this;
var needReset = this._getResetData(top).needReset;
var top = this._getResetData(top).top; if (needReset) {
scope.dragEl.animate({
'-webkit-transform': 'translate3d(0, ' + top + 'px, 0)' }, 50, 'linear', function () {
scope._reset(top);
scope._setScrollTop(top); });
} else {
scope._reset(top);
}
},
_reset: function (top) {
this.oTop = top;
this.curTop = top;
this.isMoveing = false;
this.cooling = false; //关闭冷却时间
},
//暂时仅用于,操作Y值
_cssTranslate: function (el, y) {
if (!el) return 0;
if (typeof y == 'number') {
el.css('-webkit-transform', 'translate3d(0, ' + y + 'px, 0)');
}
var data = /\((.*)\)/.exec(el.css('-webkit-transform'));
if (data && typeof data[1] == 'string') data = data[1].split(',');
if (data && typeof data[1] == 'string') return parseInt(data[1]);
return 0;
},
_needFocus: function (el) {
switch (el.nodeName.toLowerCase()) {
case 'textarea':
case 'select':
return true;
case 'input':
switch (el.type) {
case 'button':
case 'checkbox':
case 'file':
case 'image':
case 'radio':
case 'submit':
return false;
}
return !el.disabled && !el.readOnly;
default:
return (/\bneedfocus\b/).test(el.className);
}
},
//获取鼠标信息
getMousePos: function (event) {
var top, left;
top = Math.max(document.body.scrollTop, document.documentElement.scrollTop);
left = Math.max(document.body.scrollLeft, document.documentElement.scrollLeft);
return {
top: top + event.clientY,
left: left + event.clientX
};
}
};
new Scroll({ wrapper: $('#body'), body: $('#wrapper') });
</script>
</body>
</html>

如此,文本类标签便可以获得焦点了,其它的东西,各位通过代码自己搞下吧,现在只剩下停止动画了......有点累

核心代码:

 if (this._needFocus(this.clickEl)) {
$(this.clickEl).focus();
return;
}
_needFocus: function (el) {
switch (el.nodeName.toLowerCase()) {
case 'textarea':
case 'select':
return true;
case 'input':
switch (el.type) {
case 'button':
case 'checkbox':
case 'file':
case 'image':
case 'radio':
case 'submit':
return false;
}
return !el.disabled && !el.readOnly;
default:
return (/\bneedfocus\b/).test(el.className);
}
},

结语

今天,我们一起实现了简单的iScroll的功能,明天我们一起来进行源码学习,看看iScroll到底有何优点

如果您发现文中有何问题,请与我联系。

【iScroll源码学习00】模拟iScroll的更多相关文章

  1. 【iScroll源码学习03】iScroll事件机制与滚动条的实现

    前言 想不到又到周末了,周末的时间要抓紧学习才行,前几天我们学习了iScroll几点基础知识: 1. [iScroll源码学习02]分解iScroll三个核心事件点 2. [iScroll源码学习01 ...

  2. 【iScroll源码学习04】分离IScroll核心

    前言 最近几天我们前前后后基本将iScroll源码学的七七八八了,文章中未涉及的各位就要自己去看了 1. [iScroll源码学习03]iScroll事件机制与滚动条的实现 2. [iScroll源码 ...

  3. 【iScroll源码学习02】分解iScroll三个核心事件点

    前言 最近两天看到很多的总结性发言,我想想今年好像我的变化挺大的,是不是该晚上来水一发呢?嗯,决定了,晚上来水一发! 上周六,我们简单模拟了下iScroll的实现,周日我们开始了学习iScroll的源 ...

  4. 【iScroll源码学习01】准备阶段 - 叶小钗

    [iScroll源码学习01]准备阶段 - 叶小钗 时间 2013-12-29 18:41:00 博客园-原创精华区 原文  http://www.cnblogs.com/yexiaochai/p/3 ...

  5. 【iScroll源码学习01】准备阶段

    前言 我们昨天初步了解了为什么会出现iScroll:[SPA]移动站点APP化研究之上中下页面的iScroll化(上),然后简单的写了一个demo来模拟iScroll,其中了解到了以下知识点: ① v ...

  6. iscroll源码学习&lpar;1&rpar;

    iscroll是移端端开发的两大利器之一(另一个是fastclick),为了将它整合的avalon,需要对它认真学习一番.下面是我的笔记. 第一天看的是它的工具类util.js //用于做函数节流 v ...

  7. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  8. 【 js 基础 】【 源码学习 】源码设计 (持续更新)

    学习源码,除了学习对一些方法的更加聪明的代码实现,同时也要学习源码的设计,把握整体的架构.(推荐对源码有一定熟悉了之后,再看这篇文章) 目录结构:第一部分:zepto 设计分析第二部分:undersc ...

  9. Redis源码学习:Lua脚本

    Redis源码学习:Lua脚本 1.Sublime Text配置 我是在Win7下,用Sublime Text + Cygwin开发的,配置方法请参考<Sublime Text 3下C/C++开 ...

随机推荐

  1. 大型网站提速关键技术&lpar;页面静态化&comma;memcached&comma;MySql优化&rpar;&lpar;一&rpar;

    一:关键技术介绍: 衡量是否为大型网站的要素: A:PV值(page views 页面浏览量) 访问量大: 带来的问题:1:流量大 -->解决方案:增加带宽,优化程序(视频和图片较浪费带宽,尽量 ...

  2. python time模块和datetime模块详解

    一.time模块 time模块中时间表现的格式主要有三种: a.timestamp时间戳,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量 b.struct_time时间元组,共 ...

  3. 几个前端博客 good

    http://www.cnblogs.com/JustinYoung/archive/2011/02/24/fresh-free-html-templates-2010.html http://www ...

  4. Sharepoint 2016 配置FBA(三)配置Sharepoint

    编辑已经创建好的Web Applicaiton, SharePoint Central Administration -> Application Management -> Manage ...

  5. scp出现Permission denied&comma;please try again的解决办法

    当scp的时候我们发现错误,被拒绝,是因为ssh的权限问题,需要修改权限,进入到/etc/ssh文件夹下,用root用户修改文件sshd_config 将PermitRootLogin no / wi ...

  6. Win10系列:VC&plus;&plus; 定时器

    计时器机制俗称"心跳",表示以特定的频率持续触发特定事件和执行特定程序的机制.在开发Windows应用商店应用的过程中,可以使用定义在Windows::UI::Xaml命名空间中的 ...

  7. qt 使用多个ui文件

    一般的QT工程只有一个ui,本文记录如何在一个工程中使用多个ui文件. 参考链接: http://www.cnblogs.com/lc-cnblong/p/3182903.html 创建方法,鼠标在工 ...

  8. &lbrack;GDOI2014&rsqb;拯救莫莉斯 状压DP

    题面: 莫莉斯·乔是圣域里一个叱咤风云的人物,他凭借着自身超强的经济头脑,牢牢控制了圣域的石油市场. 圣域的地图可以看成是一个n*m的矩阵.每个整数坐标点(x , y)表示一座城市( 1\le x\l ...

  9. python socket 多人聊天室

    参考来源(其实我从上面复制了一点):Python 的 Socket 编程教程  http://www.oschina.net/question/12_76126Python线程指南 http://ww ...

  10. SSAS IIS 发布

    http://www.cnblogs.com/zhangzt/p/4046259.html IIS7下配置SSAS通过HTTP远程连接 淘宝 问答 学院 博客 资源下载 高端培训            ...