仿iphone日历插件(beta)

简介:
前言

小伙伴们好,很久不见了。最近工作进入正常期了,所以慢慢的悠闲的时间久没有了,所以不能每天水一篇了。

最近也在听师傅(http://home.cnblogs.com/u/aaronjs/)的教导开始阅读jquery源码了,怎么说呢,阅读的效果其实不是太好。

一来是时间断断续续的没有接上,今天读完明天又忘了,到第三天再读的话,就很多都忘记了;

二来是jquery其实还是有一定难度,加之篇幅也很长,所以读起来还是有一点吃力(我甚至有时候有种想睡的感觉),过了2星期才陆陆续续把core读完,结果很多都无法理解,再加油吧。

反正今年的目标就是把jquery读懂,时间多,不着急了。

时间比较紧未做兼容处理,请使用手机/或者使用chrome开启touch功能查看,后期补上兼容方案,以及修复BUG

关于工作

最近工作上需要在我们的网页上加入一些动画:

① 页面的切入切出的转场动画

② 模仿一个iphone的日历控件

转场动画做的时候其实碰到了很多坑,而且最后做出的效果也一般,因为既有的框架与dom结构已经出来了好久了,改不得,而且就算改了效果也不能保证好,所以暂时放下

这里说的仿iphone日历控件,不如说模仿一个单选框来的实在,而且我这里说是插件,完全就算标题党了,各位可以忽视,所以今日正题吧。

iphone的感觉

第一步我们要找到iphone的感觉,那么iphone是个什么感觉呢:



大概是这么个感觉,但是我做出来却变成了这种感觉,将就着看吧:



总体思路

PS:现在其实正在写功能代码,所以,现在我们是边写边做的,思路乱了各位请包涵

测试代码

这个代码很烂,各位就不要看了,我都不知道怎么写出来的,后面点整理吧,里面的CSS不想写就直接拿别人的用了

 View Code
demo请按照图示观看

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



关于iscroll

最开始我们总是喜欢找一些已经存在了的解决方案来试试,这样就比较简单了,但是iscroll有一定问题就是他太大了。

最简单的压缩了都快10k了,所以直接给毙了,这里暂时就用不到他了。

zepto的touch事件

我是一个喜欢偷懒的人,我看着zepto有touch事件,本来想拿来直接用用谁知道。。。

 View Code
这个就是zepto的touch事件,可以看到他只是对touchend有所监控,会触发一点点事件,所以和我们没有一毛钱关系

在此我便认命的自己敲下了以下代码:

1 document.addEventListener("touchstart", touchStart, false);
2 document.addEventListener("touchend", touchEnd, false);
3 document.addEventListener("touchmove", touchMove, false);
鼠标拖动

该功能的第一个技术点,便是元素跟着鼠标移动,拖到哪里就是哪里,这个大家都比较熟悉了,就不多说

touchend

当拖动结束时,我们要做很多后续的工作:

① 让选项进入既定的轨道

② 是否具有动画效果

③ 动画效果的步长等

PS:开始觉得有几个点可以说说,结果真的写出来却无话可说了,哎。。。。。。

经过以上版本,我们的粗制滥造版便出现了,就是以上的代码。

封装

于是我们就可以在这个基础上整理代码,封装起来了,请看下一步。

整理封装

首先,我们虽然写的不是插件,但是还是应该让他有插件的样子,来点简单的修饰吧。

经过一阶段的休整,我们的代码成了这个样子了:

 View Code
http://sandbox.runjs.cn/show/biousc1u

其中动画步长可以自己调节,选一个自己认为合适的,下面接着更新。

设置/获取

 View Code
我们这里简单看看代码:

复制代码
  1 (function () {
  2     //!!!由于动画原因,获取值可能会出现不准确的情况,比如正在动画却已经取值了
  3     //所以设置了一个冷却时间,在冷却时间的情况下设置值等操作不能进行
  4     //为了保证唯一性,全部使用index作为索引算了
  5     var ScrollRadio = function (opts) {
  6         opts = opts || {};
  7         //容器元素
  8         this.wrapper = opts.wrapper || $(document);
  9         this.body = [
 10     '<div class="cui-roller">',
 11         '<ul class="ul-list" style=" position: absolute; width: 100%; z-index: 2;  " >',
 12         '</ul>',
 13         '<div class="cui-mask"></div>',
 14         '<div class="cui-lines">&nbsp;</div>',
 15     '</div>'
 16 ].join('');
 17         this.body = $(this.body);
 18 
 19         //真正拖动的元素(现在是ul)
 20         this.dragEl = this.body.find('.ul-list');
 21         //数据源
 22         this.data = opts.data || [];
 23         this._changed = opts.changed || null;
 24         //当前选项索引默认选择2项
 25         this.selectedIndex = 1;
 26 
 27         //当前选项值
 28         //                this.key = '';
 29         //当前选项显示的值
 30         //                this.value = '';
 31 
 32         /*
 33         定位实际需要用到的信息
 34         暂时不考虑水平移动吧
 35         */
 36         this.itemHeight = 0; //单个item高度
 37         this.dragHeight = 0; //拖动元素高度
 38         this.dragTop = 0; //拖动元素top
 39         this.animateParam = [10, 6, 2, 1, 0, 0, 0, 0, 0, 0, 0]; //动画参数
 40         this.timeGap = 0; //时间间隔
 41         this.touchTime = 0; //开始时间
 42         this.moveAble = false; //是否正在移动
 43         this.moveState = 'up'; //移动状态,up right down left
 44         this.oTop = 0; //拖动前的top值
 45         this.curTop = 0; //当前容器top
 46         this.mouseY = 0; //鼠标第一次点下时相对父容器的位置
 47         this.cooling = false; //是否处于冷却时间
 48 
 49         this.init();
 50     };
 51     ScrollRadio.prototype = {
 52         constructor: ScrollRadio,
 53         init: function () {
 54             this.initItem();
 55             this.wrapper.append(this.body);
 56             this.initEventParam();
 57             this.bindEvent();
 58 
 59             return this;
 60         },
 61         //增加数据
 62         initItem: function () {
 63             var _tmp, _data, i, k;
 64             for (var i in this.data) {
 65                 _data = this.data[i]
 66                 _tmp = $('<li>' + (_data.val == undefined ? i : _data.val) + '</li>');
 67                 _tmp.attr('data-index', i);
 68                 for (k in _data) {
 69                     _tmp.attr('data-' + k, _data[k]);
 70                 }
 71                 this.dragEl.append(_tmp);
 72             }
 73         },
 74         //初始化事件需要用到的参数信息
 75         initEventParam: function () {
 76             var offset = this.dragEl.offset();
 77             var itemOffset = this.dragEl.find('li').eq(0).offset();
 78             this.itemHeight = itemOffset.height
 79             this.dragTop = offset.top;
 80             this.dragHeight = this.dragEl[0].scrollHeight;
 81             var s = '';
 82         },
 83         bindEvent: function () {
 84             var scope = this;
 85             document.addEventListener("touchstart", function (e) {
 86                 scope.touchStart.call(scope, e);
 87             }, false);
 88             document.addEventListener("touchend", function (e) {
 89                 scope.touchEnd.call(scope, e);
 90             }, false);
 91             document.addEventListener("touchmove", function (e) {
 92                 scope.touchMove.call(scope, e);
 93             }, false);
 94         },
 95         touchStart: function (e) {
 96             if (this.cooling) return false; //冷却时间不能开始
 97 
 98             //需要判断是否是拉取元素,此处需要递归验证,这里暂时不管
 99             //!!!!!!!!此处不严谨
100             var el = $(e.srcElement).parent(), pos;
101             if (el.hasClass('ul-list')) {
102                 this.moveAble = true;
103 
104                 this.touchTime = e.timeStamp;
105                 //获取鼠标信息
106                 pos = this.getMousePos(e.changedTouches[0]);
107                 //注意,此处是相对位置,注意该处还与动画有关,所以高度必须动态计算
108                 //可以设置一个冷却时间参数,但想想还是算了
109                 //最后还是使用了冷却时间
110                 //                        var top = parseFloat(this.dragEl.css('top')) || 0;
111                 //                        this.mouseY = pos.top - top;
112                 this.mouseY = pos.top - this.curTop;
113                 this.moveAble = true;
114             }
115         },
116         touchMove: function (e) {
117             if (!this.moveAble) return false;
118             var pos = this.getMousePos(e.changedTouches[0]);
119             //先获取相对容器的位置,在将两个鼠标位置相减
120             this.curTop = pos.top - this.mouseY;
121             this.dragEl.css('top', this.curTop + 'px');
122             e.preventDefault();
123         },
124         touchEnd: function (e) {
125             if (!this.moveAble) return false;
126             this.cooling = true; //开启冷却时间
127 
128             //时间间隔
129             var scope = this;
130             this.timeGap = e.timeStamp - this.touchTime;
131             var flag = this.oTop <= this.curTop ? 1 : -1; //判断是向上还是向下滚动
132             var flag2 = this.curTop > 0 ? 1 : -1; //这个会影响后面的计算结果
133             this.moveState = flag > 0 ? 'up' : 'down';
134             var ih = parseFloat(this.itemHeight);
135             var ih1 = ih / 2;
136 
137             var top = Math.abs(this.curTop);
138             var mod = top % ih;
139             top = (parseInt(top / ih) * ih + (mod > ih1 ? ih : 0)) * flag2;
140 
141             var step = parseInt(this.timeGap / 50);
142             step = step > 0 ? step : 0;
143             var speed = this.animateParam[step] || 0;
144             var increment = speed * ih * flag
145             top += increment;
146             //!!!此处动画可能导致数据不同步,后期改造需要加入冷却时间
147             if (this.oTop != this.curTop) {
148                 this.dragEl.animate({
149                     top: top + 'px'
150                 }, 100 + (speed * 20), 'ease-out', function () {
151                     var _top = top, t = false; ;
152                     if (top > ih) {
153                         _top = ih;
154                         t = true;
155                     }
156                     if (top < 0 && (top + scope.dragHeight < ih * 2)) {
157                         t = true;
158                         _top = (scope.dragHeight - ih * 2) * (-1);
159                     }
160                     if (t) {
161                         scope.dragEl.animate({
162                             top: _top + 'px'
163                         }, 10, 'ease-in-out', function () {
164                             scope.oTop = _top;
165                             scope.curTop = _top;
166                             scope.cooling = false; //关闭冷却时间
167                             scope.onTouchEnd();
168                         });
169                     } else {
170                         scope.cooling = false; //关闭冷却时间
171                         scope.oTop = top;
172                         scope.curTop = top;
173                         scope.onTouchEnd();
174                     }
175                 });
176             } else {
177                 this.cooling = false; //关闭冷却时间
178                 this.onTouchEnd();
179             }
180             this.moveAble = false;
181         },
182         onTouchEnd: function () {
183             var i = parseInt((this.curTop - this.itemHeight) / parseFloat(this.itemHeight));
184             this.selectedIndex = Math.abs(i);
185             var secItem = this.data[this.selectedIndex];
186             //触发变化事件
187             var changed = this._changed;
188             if (changed && typeof changed == 'function') {
189                 changed.call(this, secItem);
190             }
191             console.log(this.selectedIndex, secItem);
192         },
193         setKey: function (k) { },
194         setVal: function (v) { },
195         setIndex: function (i) {
196             var i = parseInt(i);
197             if (i >= this.data.length || i < 0) return false;
198 
199             this.selectedIndex = i;
200             this.curTop = (i * this.itemHeight * (-1) + this.itemHeight);
201             this.dragEl.css('top', this.curTop + 'px');
202         },
203         getSelected: function () {
204             return this.data[this.selectedIndex];
205         },
206         getByKey: function (k) { },
207         getByVal: function (v) { },
208         getByIndex: function (i) { },
209         //获取鼠标信息
210         getMousePos: function (event) {
211             var top, left;
212             top = Math.max(document.body.scrollTop, document.documentElement.scrollTop);
213             left = Math.max(document.body.scrollLeft, document.documentElement.scrollLeft);
214             return {
215                 top: top + event.clientY,
216                 left: left + event.clientX
217             };
218         }
219     };
220     window.ScrollRadio = ScrollRadio;
221 
222 })();
复制代码
以上代码基本框架都出来了,也有注释,有兴趣的朋友自己看看,我们现在进行最后一步了,将日历搞上去,老夫来不起了。。。

日历插件:)

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

 View Code
今天时间用多了,老夫有点来不起了,我们下次再完善吧。。。。。。

结语

若是各位觉得有用的话不妨用一用,发现什么BUG请留言,我下次一并更新,BUG应该很多的。 

 


本文转自叶小钗博客园博客,原文链接http://www.cnblogs.com/yexiaochai/p/3322477.html,如需转载请自行联系原作者

相关文章
|
数据安全/隐私保护 iOS开发
|
编解码 iOS开发
iphone 开发的基本入门知识
iphone 开发的基本入门知识
150 0
「镁客早报」iPhone或将在今年采用三摄;传Facebook致力于开发语音助力服务与亚马逊、苹果竞争
亚马逊向美国Alexa设备推免费音乐服务;视频会议软件开发商Zoom纳斯达克上市。
225 0
|
Web App开发 缓存 开发工具
|
存储 iOS开发 计算机视觉