仿iphone日历插件(beta)

前言

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

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

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

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

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

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

关于工作

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

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

② 模仿一个iphone的日历控件

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

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

iphone的感觉

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

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

总体思路

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

测试代码

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

 
<!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>
<style type="text/css"> /*reset*/
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,button,input,select,textarea{font:normal 14px/1.5 Tahoma,"Lucida Grande",Verdana,"Microsoft YaHei",hei;}
article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section,iframe{display:block;}
h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:500;}
address,cite,dfn,em,var,i{font-style:normal;font-weight:normal;font-family:arial;}
ul,ol{list-style:none}
a{color:#000;text-decoration:none}
a:hover{text-decoration:underline;-webkit-transition:color .2s linear;-moz-transition:color .2s linear;-ms-transition:color .2s linear;-o-transition:color .2s linear;transition:color .2s linear;}
fieldset,img,button,input{border:0}
button,input,select,textarea{font-size:100%}
table{border-collapse:collapse;border-spacing:0}
input[type="button"],input[type="submit"]{-webkit-appearance:none;}
body{min-width:320px; background:#f5f5f5;overflow-x:hidden;-webkit-text-size-adjust:none;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-font-smoothing:subpixel-antialiased;-moz-user-select:none;color:#000;}:focus{outline:none;}.clearfix:after{clear:both;content:'.';display:block;height:0;visibility:hidden;line-height:0;}.clearfix{*zoom:1;}.fl{float:left;}.fr{float:right;}.clear{clear:both;}.overflow{overflow:hidden;}.ellips{white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}.ellips_line2,.ellips_line3{display: -webkit-box;-webkit-box-orient: vertical;overflow: hidden;}.ellips_line2{-webkit-line-clamp:2;}.ellips_line3{-webkit-line-clamp:3;}.pos_rel{position:relative;}
html,body,.h100{ height:100%; }
dfn{color:#ff9913; font-size:12px; font-weight:normal;}
/*-----------------------------------------common--------------------*/
.i,.i_bef:before,.i_aft:after,.carlist li:before,.ok_crt:after{background:url(../img/style1/v2_bgs.png) no-repeat; background-size:175px 125px;}
.i_bef:before,.i_aft:after,.carlist li:before{position: absolute;content: "";}
.abs_size{-moz-box-sizing:border-box; -webkit-box-sizing:border-box; box-sizing:border-box;}
.opacity{opacity:0.7; filter:alpha(opacity=70);}
/*active*/
.list_st_border li:active,.p_hinttxt:active,.search_cancel:active,.citylist dd:active{ background:#f8f8f8;}
.btn_yellow:active{background:#eb840f;}
/*layout*/
.wrap{margin-top:48px;}
.wrap_pb{margin-top:48px; padding-bottom:45px;}
.p10{padding:10px;}
.hm{text-align:center;}
.break_all{word-break:break-all;}
.fix_b{position:fixed; bottom:0; left:0;z-index:9999;}
/*font bg color size*/
h1{font:normal 1.286em/1.5 "";}/*18px*/
h2{font:normal 1.143em/1.5 "";}/*16px*/
h3{font:600 1em/1.5 "";}/*14px*/
.price{margin-left:5px;}
.price i{margin-left:2px; font-size:1.286em;}
.greyfont{color:#686868;}
.greyfont1{color:#909090;}
.bggrey{height:100%; background:#f5f5f5;}
/*header*/
header{position:fixed; top:0; left:0; z-index:9999; width:100%; height:48px;background-color:#1491c5;}
.returnico{position:absolute; left:0; top:0; width:68px; height:48px; background-color:#15a4d5;}
.returnico:before{left:25px;top:16px;width:12px;height:20px; background-position:0 0;}
.icon_phone,.icon_home{width:42px;height:100%;top:0;position:relative;float:right;}
.icon_phone:before{width:11px;height:20px;top:16px;right:12px;background-position:-115px -65px;}
.icon_home:before{width:20px;height:19px;top:16px;right:10px;background-position:-83px -66px;}
header i:active{opacity:0.7; filter:alpha(opacity=70);}
header h1{position:absolute; width:100%;line-height:48px; text-align:center; letter-spacing:2px; color: #fff;}
.header_r,.header_rs{position:absolute;z-index:9; top:0; right:0; line-height:48px; padding:0 15px; font-size:18px; background:#15a4d5; color:#fff;}
.header_rs{padding:0 5px; font-size:12px; }
/*background-position*/
.select_n:before{width:20px;height:20px;background-position:-18px 0;}
.select_n.current:before{background-position:-44px 0;}
/*searchbox*/
.search_wrap{overflow:hidden; padding:10px; background:#dfeaf1;}
.search_box{position:relative; float:left; width:100%;}
.search_input{width:100%; padding:0 10px 0 28px; line-height:30px; background-color:#fff; border-radius:4px; color:#ccc;}
.fdj:before,.fdj:after,.search_box:before,.search_box:after{position:absolute; content:""; z-index:9;}
.fdj:before,.search_box:before{left:5px; top:7px; width:12px; height:12px; border:1px solid #bcbcbc; border-radius:12px;}
.fdj:after,.search_box:after{left:19px; top:17px; width:1px; height:8px; background:#bcbcbc;}
.search_cancel{ display:none;float:left; width:20%; line-height:30px; text-align:center; font-size:16px;color:#1491c5; background:none; border:none;}
.close_icon{display:none; position:absolute; z-index:10; top:8px; right:4px; width:16px; height:16px; border-radius:30px; background:#b1b1b1;}
.close_icon:before,.close_icon:after{position:absolute; content:""; top:4px; left:7px; width:2px; height:8px; background:#fff; }
.close_icon:before{-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg);}
.fdj:after,.search_box:after,.close_icon:after{-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg);}
.search_focus .search_box{width:80%;}
.search_focus .close_icon, .search_focus .search_cancel{display:block;}
.search_input:focus{color:#000;}
/*tab*/
.tab{ background-color:#f8f8f8; border-bottom:1px solid #dfdfdf;}
.tab li{float:left; width:50%; height:39px; line-height:39px; text-align:center; border-right:1px solid #dfdfdf;}
.tab li:last-child{border-right:none;}
.tab li.tabcrt{ background-color:#dfdfdf;}
.tab_b{ background-color:#f5f5f5; border-bottom:1px solid #c1c1c1;}
.tab_b li{float:left; width:50%; height:39px; line-height:39px; text-align:center;}
.tab_b li:last-child{border-right:none;}
.tab_b li.tabcrt{color:#1491c5; border-bottom:4px solid #1491c5;}
/*list*/
.list_st_border{ background:#fff; border-bottom:none; border:1px solid #cfcfcf; }
.list_st_border li{position:relative;padding:0 10px; line-height:42px; border-bottom:1px solid #cfcfcf;}
.list_st_border li:last-child{border-bottom:none;}
.list_sli{padding:10px; overflow:hidden; border-bottom:1px solid #cfcfcf; background:#fff;}
.list_sli .list_sunit{float:left;}
.citylist{color:#000;}
.citylist dt,.citylist dd{padding-left:10px;border-bottom:1px solid #e2e2e2;}
.citylist dt{line-height:25px; background-color:#eaeaea;}
.citylist dd{position:relative; font-size:16px; line-height:43px; background-color:#fff;}
.citylist .ok_crt{color:#1491c5;}
.citylist .ok_crt:after{position:absolute;content:""; right:10px; top:50%; margin-top:-6px; width:12px; height:13px; background-position:-110px 0;} /*arr*/
.li_arr_r{position:relative;}
.arr_r{position:absolute;right:0px; top:50%; width:30px; height:30px; margin-top:-15px; }
.arr_r:before,.arr_r:after,.li_arr_r:before,.li_arr_r:after{position: absolute; left:15px; content: "";width:2px; height:7px; background-color:#909090;}
.arr_r:before,.li_arr_r:before{top:10px;-webkit-transform:rotate(-45deg); -moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg);}
.arr_r:after,.li_arr_r:after{top:15px;-webkit-transform:rotate(45deg); -moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg);}
.li_arr_r:before,.li_arr_r:after{left:auto; right:10px; top:50%; margin-top:-5px;}
.li_arr_r:after{margin-top:0;}
/*p*/
.p_grey{margin:10px 5px; font-size:13px; color:#989898;}
.p_grey_center{ text-align:center; margin:20px 5px; font-size:13px; color:#989898;}
.p_hinttxt{padding:20px 10px; text-align:center; font-size:16px; color:#1491c5;} /*btn*/
.btn_yellow,.btn_del{width:100%; height:44px; line-height:42px; padding:0 10px; color:#fff;}
.btn_yellow{background:#ff9913;}
.btn_del{background:#ca4345;text-align:center;font-size:1.2em;}
.btn_pay{padding:0 20px;height:44px;float:right;background:#ff7d13;color:#fff;}
.btn_pay:active{background:#ff7300;}
.room_num {position:absolute; right:10px; width:100px; height:30px; line-height:30px;color:#000;background-color: #fff; text-align:center;border:#bfbfbf 1px solid;}
.room_num i{position:absolute; width:30px; height:30px; text-align:center; font:normal 2em/28px "Arial";}
.room_num i:first-child{left:0;background:#f4f4f4;color:#d9d9d9;font:normal 2.8em/25px "Arial";}
.room_num i:last-child{right:0;background:#06a2d0;color:#fff;} /*日历*/
.cui_cldwrap{color:#000;}
.cui_cldweek{height:25px; overflow:hidden;font:normal 12px/24px "verdana";border-bottom:1px solid #c8c8c8;}
.cui_cldweek li{float:left; width:14%; text-align:center; }
.cui_cldweek li:first-child,.cui_cldweek li:last-child{width:15%; color:#acacac;}
.cui_cldmonth{height:35px;text-align:center;font:normal 16px/35px "verdana"; background:#fff;}
.cui_cldunit{margin-bottom:20px;}
.cui_cld_daybox{overflow:hidden; background:#fff;}
.cui_cld_daybox li{float:left; width:14%; height:45px; padding:4px 0; font:normal 12px/45px "verdana"; overflow:hidden; text-align:center;}
.cui_cld_daybox li:nth-child(7n),.cui_cld_daybox li:nth-child(7n+1){width:15%;}
.cui_cld_daypass{ background:#f7f7f7;}
.cui_cld_daycrt{background:#06a2d0; color:#fff;}
.cui_cld_dayfuture{background:#fff;}
.cui_cld_day_nocrtmonth{ visibility:hidden;}
.cui_cld_day_havetxt em{display:block; line-height:25px;}
.cui_cld_day_havetxt i{display:block;line-height:15px;}
.cui_cld_day_hint{color:#06a2d0;}
/*全局XXXX*/
/*弹出蓝色框*/
.cui-pop-box{background:#fff;}
.cui-text-center{text-align:center;}
.cui-pop-box .cui-hd{height:40px;line-height:40px;font-size:1.2em;color:#fff;background:#1491c5;padding:0 10px;position:relative;}
.cui-pop-box .cui-hd .cui-close{position:absolute;top:10px; right:10px; width:18px; height:18px;line-height:18px;border-radius:50%; background:#fff;color:#1491c5;text-align:center;font-weight:bold;}
.cui-error{width:150px;margin:auto;border-radius:5px;background:rgba(73,73,73,1);padding:10px;color:#fff;font-weight:700;text-align:center;word-break:break-all;}
.cui-select-view li{border-bottom:#dfdfdf 1px solid;padding:.8em 2em .8em .8em;white-space:nowrap; overflow:hidden; text-overflow:ellipsis;position:relative;}
.cui-select-view li.current{color:#1084bc;}
.cui-select-view li:active{background:rgba(0,0,0,.05);}
.cui-select-view li.current:before,.cui-select-view li.current:after{position:absolute;content:"";background:#1084bc;height:3px;top:50%;border-radius:3px;}
.cui-select-view li.current:before{width:18px;right:10px;margin-top:-2px;-webkit-transform:rotate(-50deg);-moz-transform:rotate(-50deg);-ms-transform:rotate(-50deg);transform:rotate(-50deg);}
.cui-select-view li.current:after{width:9px;right:22px;margin-top:2px;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);}
.cui-roller{width:100%;height:90px;overflow:hidden;position:relative;display: -webkit-box;display: -moz-box;display: -ms-flexbox;display: -webkit-flex;display: flex;}
.cui-roller .ul-list{height:100%;text-align:center;font-size:1.1em;line-height:30px;-webkit-box-flex: 1;-moz-box-flex: 1;-webkit-flex: 1;-ms-flex: 1;flex: 1;}
.cui-mask{position:absolute;z-index:3;top: 0;left: 0;width: 100%;height: 100%;pointer-events: none;
background: linear-gradient(#fff 0,rgba(245,245,245,0) 52%,rgba(245,245,245,0) 48%,#fff 100%);
background: -webkit-gradient(linear,left bottom,left top,from(#fff),color-stop(0.52,rgba(245,245,245,0)),color-stop(0.48,rgba(245,245,245,0)),to(#fff));
background: -webkit-linear-gradient(#fff 0,rgba(245,245,245,0) 52%,rgba(245,245,245,0) 48%,#fff 100%);
background: -moz-linear-gradient(#fff 0,rgba(245,245,245,0) 52%,rgba(245,245,245,0) 48%,#fff 100%);
}
.cui-lines{width:100%;height:34px;position:absolute;top:50%;margin-top:-17px;border-top:#dfdfdf 1px solid;border-bottom:#dfdfdf 1px solid;}
.cui-roller-tips{color:#8d8d8d;padding:5px;text-align:center;}
.cui-roller-btns{background:#f6f5f5;padding:10px;text-align:center;}
.cui-roller-btns span{width:45%;display:inline-block;padding:10px 0;color:#fff;}
.cui-roller-btns span:active{opacity:.75;}
.cui-roller-btns .cui-btns-cancel{background:#a9a9a9;margin-right:5%;}
.cui-roller-btns .cui-btns-sure{background:#ff9913;}
</style>
<script src="../zepto.js" type="text/javascript"></script>
</head>
<body id="ctripPage" onselectstart="return false">
<!--滚轮-->
<div class="cui-pop-box">
<div class="cui-hd"><div class="cui-text-center">滚轮滚轮滚轮</div></div>
<div class="cui-bd">
<div class="cui-roller">
<ul class="ul-list" style=" position: absolute; width: 100%; z-index: 2; " >
<li>选项1</li>
<li>选项2</li>
<li>选项3</li>
<li>选项4</li>
<li>选项5</li>
<li>选项6</li>
<li>选项7</li>
<li>选项8</li>
<li>选项9</li>
<li>选项10</li>
<li>选项11</li>
<li>选项12</li>
<li>选项13</li>
<li>选项14</li>
<li>选项15</li>
<li>选项16</li>
<li>选项17</li>
<li>选项18</li>
<li>选项19</li>
<li>选项20</li> <li>选项1</li>
<li>选项2</li>
<li>选项3</li>
<li>选项4</li>
<li>选项5</li>
<li>选项6</li>
<li>选项7</li>
<li>选项8</li>
<li>选项9</li>
<li>选项10</li>
<li>选项11</li>
<li>选项12</li>
<li>选项13</li>
<li>选项14</li>
<li>选项15</li>
<li>选项16</li>
<li>选项17</li>
<li>选项18</li>
<li>选项19</li>
<li>选项20</li>
</ul> <div class="cui-mask" style=""></div>
<div class="cui-lines">&nbsp;</div>
</div>
<p class="cui-roller-tips">提示信息</p>
<div class="cui-roller-btns">
<span class="cui-btns-cancel">取消</span><span class="cui-btns-sure">确定</span>
</div>
</div>
</div> <script type="text/javascript">
// var myScroll = new iScroll('wl1');
  var y1 = 0, y2 = 0;
var moveAble = false;
var dragEl = null;
var parent = $('.cui-roller');
var offset;
var el_top = 0;
var o_top = 0;
var t1 = 0, t2 = 0;
var timer = null;
document.addEventListener("touchstart", touchStart, false);
document.addEventListener("touchend", touchEnd, false);
document.addEventListener("touchmove", touchMove, false);
function touchStart(e) {
t1 = e.timeStamp;
var srcElement = $(e.srcElement), pos;
dragEl = $(e.srcElement).parent();
if (dragEl.hasClass('ul-list')) {
var pos = getMousePos(e.changedTouches[0]);
offset = dragEl.offset();
y1 = pos.top - offset.top;
var top = parseFloat(dragEl.css('top')) || 0;
o_top = top;
y1 = y1 - parseFloat(top);
moveAble = true;
}
}
function touchMove(e) {
if (!dragEl || !moveAble) return false;
var pos = getMousePos(e.changedTouches[0]);
y2 = pos.top - y1 - offset.top;
if (timer) clearTimeout(timer); el_top = y2;
dragEl.css('top', y2 + 'px');
e.preventDefault();
} function touchEnd(e) {
t2 = e.timeStamp - t1;
var flag = o_top <= el_top ? 1 : -1;
var flag2 = el_top > 0 ? 1 : -1;
el_top = Math.abs(el_top);
var mod = el_top % 30;
el_top = (parseInt(el_top / 30) * 30 + (mod > 15 ? 30 : 0)) * flag2;
if (t2 >= 200) {
dragEl.animate({
top: el_top + 'px'
}, 100, 'ease-in-out', function () {
if (el_top > 30) {
dragEl.animate({
top: '30px'
}, 10, 'ease-in-out');
}
if (el_top < 0 && (el_top + dragEl[0].scrollHeight < 60)) {
dragEl.animate({
top: '-' + (dragEl[0].scrollHeight - 60) + 'px'
}, 10, 'ease-in-out');
}
});
} else {
var step = parseInt(t2 / 50);
console.log(t2, step);
var param = [10, 6, 4, 2];
var _tmp = param[step] * 30 * flag;
_tmp = el_top + _tmp;
dragEl.animate({
top: (_tmp) + 'px'
}, 100 + (param[step] * 50), 'ease-in-out', function () {
if (_tmp > 30) {
dragEl.animate({
top: '30px'
}, 10, 'ease-in-out');
}
if (_tmp < 0 && (_tmp + dragEl[0].scrollHeight < 60)) {
dragEl.animate({
top: '-' + (dragEl[0].scrollHeight - 60) + 'px'
}, 10, 'ease-in-out');
}
}); }
moveAble = false;
}
function getMousePos(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
};
};
var s = '';
</script>
</body>
</html>

demo请按照图示观看

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

关于iscroll

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

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

zepto的touch事件

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

 
// Zepto.js
// (c) 2010-2012 Thomas Fuchs
// Zepto.js may be freely distributed under the MIT license.
;(function($){
var touch = {},
touchTimeout, tapTimeout, swipeTimeout,
longTapDelay = 750, longTapTimeout
function parentIfText(node) {
return 'tagName' in node ? node : node.parentNode
}
function swipeDirection(x1, x2, y1, y2) {
var xDelta = Math.abs(x1 - x2), yDelta = Math.abs(y1 - y2)
return xDelta >= yDelta ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
}
function longTap() {
longTapTimeout = null
if (touch.last) {
touch.el.trigger('longTap')
touch = {}
}
}
function cancelLongTap() {
if (longTapTimeout) clearTimeout(longTapTimeout)
longTapTimeout = null
}
function cancelAll() {
if (touchTimeout) clearTimeout(touchTimeout)
if (tapTimeout) clearTimeout(tapTimeout)
if (swipeTimeout) clearTimeout(swipeTimeout)
if (longTapTimeout) clearTimeout(longTapTimeout)
touchTimeout = tapTimeout = swipeTimeout = longTapTimeout = null
touch = {}
}
$(document).ready(function(){
var now, delta
$(document.body)
.bind('touchstart', function(e){
now = Date.now()
delta = now - (touch.last || now)
touch.el = $(parentIfText(e.touches[0].target))
touchTimeout && clearTimeout(touchTimeout)
touch.x1 = e.touches[0].pageX
touch.y1 = e.touches[0].pageY
if (delta > 0 && delta <= 250) touch.isDoubleTap = true
touch.last = now
longTapTimeout = setTimeout(longTap, longTapDelay)
})
.bind('touchmove', function(e){
cancelLongTap()
touch.x2 = e.touches[0].pageX
touch.y2 = e.touches[0].pageY
if (Math.abs(touch.x1 - touch.x2) > 10)
e.preventDefault()
})
.bind('touchend', function(e){
cancelLongTap()
// swipe
if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) ||
(touch.y2 && Math.abs(touch.y1 - touch.y2) > 30))
swipeTimeout = setTimeout(function() {
touch.el.trigger('swipe')
touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)))
touch = {}
}, 0)
// normal tap
else if ('last' in touch)
// delay by one tick so we can cancel the 'tap' event if 'scroll' fires
// ('tap' fires before 'scroll')
tapTimeout = setTimeout(function() {
// trigger universal 'tap' with the option to cancelTouch()
// (cancelTouch cancels processing of single vs double taps for faster 'tap' response)
var event = $.Event('tap')
event.cancelTouch = cancelAll
touch.el.trigger(event)
// trigger double tap immediately
if (touch.isDoubleTap) {
touch.el.trigger('doubleTap')
touch = {}
}
// trigger single tap after 250ms of inactivity
else {
touchTimeout = setTimeout(function(){
touchTimeout = null
touch.el.trigger('singleTap')
touch = {}
}, 250)
}
}, 0)
})
.bind('touchcancel', cancelAll)
$(window).bind('scroll', cancelAll)
})
;['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function(m){
$.fn[m] = function(callback){ return this.bind(m, callback) }
})
})(Zepto)

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

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

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

鼠标拖动

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

touchend

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

① 让选项进入既定的轨道

② 是否具有动画效果

③ 动画效果的步长等

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

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

封装

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

整理封装

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

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

(function () {
var ScrollRadio = function (opts) {
opts = opts || {};
//容器元素
this.wrapper = opts.wrapper || $(document);
this.body = [
'<div class="cui-roller">',
'<ul class="ul-list" style=" position: absolute; width: 100%; z-index: 2; " >',
'</ul>',
'<div class="cui-mask"></div>',
'<div class="cui-lines">&nbsp;</div>',
'</div>'
].join('');
this.body = $(this.body);

//真正拖动的元素(现在是ul)
this.dragEl = this.body.find('.ul-list');
//数据源
this.data = opts.data || [];
this._changed = opts.changed || null;
//当前选项索引
this.selectedIndex = 0;
//当前选项值
this.key = '';
//当前选项显示的值
this.value = '';

/*
定位实际需要用到的信息
暂时不考虑水平移动吧
*/
this.itemHeight = 0; //单个item高度
this.dragHeight = 0; //拖动元素高度
this.dragTop = 0; //拖动元素top
this.animateParam = [10, 8, 7, 6, 6, 6, 5, 5, 4, 2, 0]; //动画参数
this.timeGap = 0; //时间间隔
this.touchTime = 0; //开始时间
this.moveAble = false; //是否正在移动
this.moveState = 'up'; //移动状态,up right down left
this.oTop = 0; //拖动前的top值
this.curTop = 0; //当前容器top
this.mouseY = 0; //鼠标第一次点下时相对父容器的位置

this.init();
};
ScrollRadio.prototype = {
constructor: ScrollRadio,
init: function () {
this.initItem();
this.wrapper.append(this.body);
this.initEventParam();
this.bindEvent();

return this;
},
//增加数据
initItem: function () {
var _tmp, _data, i, k;
for (var i in this.data) {
_data = this.data[i]
_tmp = $('<li>' + (_data.val == undefined ? i : _data.val) + '</li>');
_tmp.attr('data-index', i);
for (k in _data) {
_tmp.attr('data-' + k, _data[k]);
}
this.dragEl.append(_tmp);
}
},
//初始化事件需要用到的参数信息
initEventParam: function () {
var offset = this.dragEl.offset();
var itemOffset = this.dragEl.find('li').eq(0).offset();
this.itemHeight = itemOffset.height
this.dragTop = offset.top;
this.dragHeight = this.dragEl[0].scrollHeight;
var s = '';
},
bindEvent: function () {
var scope = this;
document.addEventListener("touchstart", function (e) {
scope.touchStart.call(scope, e);
}, false);
document.addEventListener("touchend", function (e) {
scope.touchEnd.call(scope, e);
}, false);
document.addEventListener("touchmove", function (e) {
scope.touchMove.call(scope, e);
}, false);
},
touchStart: function (e) {
//需要判断是否是拉取元素,此处需要递归验证,这里暂时不管
//!!!!!!!!此处不严谨
var el = $(e.srcElement).parent(), pos;
if (el.hasClass('ul-list')) {
this.touchTime = e.timeStamp;
//获取鼠标信息
pos = this.getMousePos(e.changedTouches[0]);
//注意,此处是相对位置
this.mouseY = pos.top - this.curTop;
this.moveAble = true;
}
},
touchMove: function (e) {
if (!this.moveAble) return false;
var pos = this.getMousePos(e.changedTouches[0]);
//先获取相对容器的位置,在将两个鼠标位置相减
this.curTop = pos.top - this.mouseY;

this.dragEl.css('top', this.curTop + 'px');

e.preventDefault();
},
touchEnd: function (e) {
//时间间隔
var scope = this;
this.timeGap = e.timeStamp - this.touchTime;
var flag = this.oTop <= this.curTop ? 1 : -1; //判断是向上还是向下滚动
var flag2 = this.curTop > 0 ? 1 : -1; //这个会影响后面的计算结果
this.moveState = flag > 0 ? 'up' : 'down';
var ih = parseFloat(this.itemHeight);
var ih1 = ih / 2;

var top = Math.abs(this.curTop);
var mod = top % ih;
top = (parseInt(top / ih) * ih + (mod > ih1 ? ih : 0)) * flag2;

var step = parseInt(this.timeGap / 10 - 10);
step = step > 0 ? step : 0;
var speed = this.animateParam[step] || 0;
var increment = speed * ih * flag
top += increment;
this.dragEl.animate({
top: top + 'px'
}, 100 + (speed * 20), 'ease-in-out', function () {
if (top > ih) {
scope.dragEl.animate({
top: ih + 'px'
}, 10, 'ease-in-out');
}
if (top < 0 && (top + scope.dragHeight < ih * 2)) {
scope.dragEl.animate({
top: '-' + (scope.dragHeight - ih * 2) + 'px'
}, 10, 'ease-in-out');
}
});

console.log(this.timeGap, top, increment);

this.oTop = top;
this.curTop = top;
this.moveAble = false;

},
setKey: function (k) {

},
setVal: function (v) { },
setIndex: function (i) {

},
getSelected: function () {

},
getByKey: function (k) { },
getByVal: function (v) { },
getByIndex: function (i) { },

//选项改变时候触发
changed: function () {

},
//获取鼠标信息
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
};
}

};

window.ScrollRadio = ScrollRadio;

})();

var data = [];
for (var i = 0; i < 100; i++) {
var temp = { val: '选项-' + i };
data.push(temp);
}

var myScroll = new ScrollRadio({
wrapper: $('#ctripPage'),
data: data
});

var s = '';

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

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

设置/获取

<!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>
<style type="text/css">

/*reset*/
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,button,input,select,textarea{font:normal 14px/1.5 Tahoma,"Lucida Grande",Verdana,"Microsoft YaHei",hei;}
article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section,iframe{display:block;}
h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:500;}
address,cite,dfn,em,var,i{font-style:normal;font-weight:normal;font-family:arial;}
ul,ol{list-style:none}
a{color:#000;text-decoration:none}
a:hover{text-decoration:underline;-webkit-transition:color .2s linear;-moz-transition:color .2s linear;-ms-transition:color .2s linear;-o-transition:color .2s linear;transition:color .2s linear;}
fieldset,img,button,input{border:0}
button,input,select,textarea{font-size:100%}
table{border-collapse:collapse;border-spacing:0}
input[type="button"],input[type="submit"]{-webkit-appearance:none;}
body{min-width:320px; background:#f5f5f5;overflow-x:hidden;-webkit-text-size-adjust:none;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-font-smoothing:subpixel-antialiased;-moz-user-select:none;color:#000;}:focus{outline:none;}.clearfix:after{clear:both;content:'.';display:block;height:0;visibility:hidden;line-height:0;}.clearfix{*zoom:1;}.fl{float:left;}.fr{float:right;}.clear{clear:both;}.overflow{overflow:hidden;}.ellips{white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}.ellips_line2,.ellips_line3{display: -webkit-box;-webkit-box-orient: vertical;overflow: hidden;}.ellips_line2{-webkit-line-clamp:2;}.ellips_line3{-webkit-line-clamp:3;}.pos_rel{position:relative;}
html,body,.h100{ height:100%; }
dfn{color:#ff9913; font-size:12px; font-weight:normal;}
/*-----------------------------------------common--------------------*/
.i,.i_bef:before,.i_aft:after,.carlist li:before,.ok_crt:after{background:url(../img/style1/v2_bgs.png) no-repeat; background-size:175px 125px;}
.i_bef:before,.i_aft:after,.carlist li:before{position: absolute;content: "";}
.abs_size{-moz-box-sizing:border-box; -webkit-box-sizing:border-box; box-sizing:border-box;}
.opacity{opacity:0.7; filter:alpha(opacity=70);}

/*active*/
.list_st_border li:active,.p_hinttxt:active,.search_cancel:active,.citylist dd:active{ background:#f8f8f8;}
.btn_yellow:active{background:#eb840f;}

/*layout*/
.wrap{margin-top:48px;}
.wrap_pb{margin-top:48px; padding-bottom:45px;}
.p10{padding:10px;}
.hm{text-align:center;}
.break_all{word-break:break-all;}
.fix_b{position:fixed; bottom:0; left:0;z-index:9999;}

/*font bg color size*/
h1{font:normal 1.286em/1.5 "";}/*18px*/
h2{font:normal 1.143em/1.5 "";}/*16px*/
h3{font:600 1em/1.5 "";}/*14px*/
.price{margin-left:5px;}
.price i{margin-left:2px; font-size:1.286em;}
.greyfont{color:#686868;}
.greyfont1{color:#909090;}
.bggrey{height:100%; background:#f5f5f5;}

/*header*/
header{position:fixed; top:0; left:0; z-index:9999; width:100%; height:48px;background-color:#1491c5;}
.returnico{position:absolute; left:0; top:0; width:68px; height:48px; background-color:#15a4d5;}
.returnico:before{left:25px;top:16px;width:12px;height:20px; background-position:0 0;}
.icon_phone,.icon_home{width:42px;height:100%;top:0;position:relative;float:right;}
.icon_phone:before{width:11px;height:20px;top:16px;right:12px;background-position:-115px -65px;}
.icon_home:before{width:20px;height:19px;top:16px;right:10px;background-position:-83px -66px;}
header i:active{opacity:0.7; filter:alpha(opacity=70);}
header h1{position:absolute; width:100%;line-height:48px; text-align:center; letter-spacing:2px; color: #fff;}
.header_r,.header_rs{position:absolute;z-index:9; top:0; right:0; line-height:48px; padding:0 15px; font-size:18px; background:#15a4d5; color:#fff;}
.header_rs{padding:0 5px; font-size:12px; }

/*background-position*/
.select_n:before{width:20px;height:20px;background-position:-18px 0;}
.select_n.current:before{background-position:-44px 0;}

/*searchbox*/
.search_wrap{overflow:hidden; padding:10px; background:#dfeaf1;}
.search_box{position:relative; float:left; width:100%;}
.search_input{width:100%; padding:0 10px 0 28px; line-height:30px; background-color:#fff; border-radius:4px; color:#ccc;}
.fdj:before,.fdj:after,.search_box:before,.search_box:after{position:absolute; content:""; z-index:9;}
.fdj:before,.search_box:before{left:5px; top:7px; width:12px; height:12px; border:1px solid #bcbcbc; border-radius:12px;}
.fdj:after,.search_box:after{left:19px; top:17px; width:1px; height:8px; background:#bcbcbc;}
.search_cancel{ display:none;float:left; width:20%; line-height:30px; text-align:center; font-size:16px;color:#1491c5; background:none; border:none;}
.close_icon{display:none; position:absolute; z-index:10; top:8px; right:4px; width:16px; height:16px; border-radius:30px; background:#b1b1b1;}
.close_icon:before,.close_icon:after{position:absolute; content:""; top:4px; left:7px; width:2px; height:8px; background:#fff; }
.close_icon:before{-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg);}
.fdj:after,.search_box:after,.close_icon:after{-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg);}
.search_focus .search_box{width:80%;}
.search_focus .close_icon, .search_focus .search_cancel{display:block;}
.search_input:focus{color:#000;}

/*tab*/
.tab{ background-color:#f8f8f8; border-bottom:1px solid #dfdfdf;}
.tab li{float:left; width:50%; height:39px; line-height:39px; text-align:center; border-right:1px solid #dfdfdf;}
.tab li:last-child{border-right:none;}
.tab li.tabcrt{ background-color:#dfdfdf;}

.tab_b{ background-color:#f5f5f5; border-bottom:1px solid #c1c1c1;}
.tab_b li{float:left; width:50%; height:39px; line-height:39px; text-align:center;}
.tab_b li:last-child{border-right:none;}
.tab_b li.tabcrt{color:#1491c5; border-bottom:4px solid #1491c5;}

/*list*/
.list_st_border{ background:#fff; border-bottom:none; border:1px solid #cfcfcf; }
.list_st_border li{position:relative;padding:0 10px; line-height:42px; border-bottom:1px solid #cfcfcf;}
.list_st_border li:last-child{border-bottom:none;}

.list_sli{padding:10px; overflow:hidden; border-bottom:1px solid #cfcfcf; background:#fff;}
.list_sli .list_sunit{float:left;}

.citylist{color:#000;}
.citylist dt,.citylist dd{padding-left:10px;border-bottom:1px solid #e2e2e2;}
.citylist dt{line-height:25px; background-color:#eaeaea;}
.citylist dd{position:relative; font-size:16px; line-height:43px; background-color:#fff;}
.citylist .ok_crt{color:#1491c5;}
.citylist .ok_crt:after{position:absolute;content:""; right:10px; top:50%; margin-top:-6px; width:12px; height:13px; background-position:-110px 0;}

/*arr*/
.li_arr_r{position:relative;}
.arr_r{position:absolute;right:0px; top:50%; width:30px; height:30px; margin-top:-15px; }
.arr_r:before,.arr_r:after,.li_arr_r:before,.li_arr_r:after{position: absolute; left:15px; content: "";width:2px; height:7px; background-color:#909090;}
.arr_r:before,.li_arr_r:before{top:10px;-webkit-transform:rotate(-45deg); -moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg);}
.arr_r:after,.li_arr_r:after{top:15px;-webkit-transform:rotate(45deg); -moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg);}
.li_arr_r:before,.li_arr_r:after{left:auto; right:10px; top:50%; margin-top:-5px;}
.li_arr_r:after{margin-top:0;}

/*p*/
.p_grey{margin:10px 5px; font-size:13px; color:#989898;}
.p_grey_center{ text-align:center; margin:20px 5px; font-size:13px; color:#989898;}
.p_hinttxt{padding:20px 10px; text-align:center; font-size:16px; color:#1491c5;}

/*btn*/
.btn_yellow,.btn_del{width:100%; height:44px; line-height:42px; padding:0 10px; color:#fff;}
.btn_yellow{background:#ff9913;}
.btn_del{background:#ca4345;text-align:center;font-size:1.2em;}

.btn_pay{padding:0 20px;height:44px;float:right;background:#ff7d13;color:#fff;}
.btn_pay:active{background:#ff7300;}
.room_num {position:absolute; right:10px; width:100px; height:30px; line-height:30px;color:#000;background-color: #fff; text-align:center;border:#bfbfbf 1px solid;}
.room_num i{position:absolute; width:30px; height:30px; text-align:center; font:normal 2em/28px "Arial";}
.room_num i:first-child{left:0;background:#f4f4f4;color:#d9d9d9;font:normal 2.8em/25px "Arial";}
.room_num i:last-child{right:0;background:#06a2d0;color:#fff;}

/*日历*/
.cui_cldwrap{color:#000;}
.cui_cldweek{height:25px; overflow:hidden;font:normal 12px/24px "verdana";border-bottom:1px solid #c8c8c8;}
.cui_cldweek li{float:left; width:14%; text-align:center; }
.cui_cldweek li:first-child,.cui_cldweek li:last-child{width:15%; color:#acacac;}
.cui_cldmonth{height:35px;text-align:center;font:normal 16px/35px "verdana"; background:#fff;}
.cui_cldunit{margin-bottom:20px;}
.cui_cld_daybox{overflow:hidden; background:#fff;}
.cui_cld_daybox li{float:left; width:14%; height:45px; padding:4px 0; font:normal 12px/45px "verdana"; overflow:hidden; text-align:center;}
.cui_cld_daybox li:nth-child(7n),.cui_cld_daybox li:nth-child(7n+1){width:15%;}
.cui_cld_daypass{ background:#f7f7f7;}
.cui_cld_daycrt{background:#06a2d0; color:#fff;}
.cui_cld_dayfuture{background:#fff;}
.cui_cld_day_nocrtmonth{ visibility:hidden;}
.cui_cld_day_havetxt em{display:block; line-height:25px;}
.cui_cld_day_havetxt i{display:block;line-height:15px;}
.cui_cld_day_hint{color:#06a2d0;}
/*全局XXXX*/

/*弹出蓝色框*/
.cui-pop-box{background:#fff;}
.cui-text-center{text-align:center;}
.cui-pop-box .cui-hd{height:40px;line-height:40px;font-size:1.2em;color:#fff;background:#1491c5;padding:0 10px;position:relative;}
.cui-pop-box .cui-hd .cui-close{position:absolute;top:10px; right:10px; width:18px; height:18px;line-height:18px;border-radius:50%; background:#fff;color:#1491c5;text-align:center;font-weight:bold;}
.cui-error{width:150px;margin:auto;border-radius:5px;background:rgba(73,73,73,1);padding:10px;color:#fff;font-weight:700;text-align:center;word-break:break-all;}
.cui-select-view li{border-bottom:#dfdfdf 1px solid;padding:.8em 2em .8em .8em;white-space:nowrap; overflow:hidden; text-overflow:ellipsis;position:relative;}
.cui-select-view li.current{color:#1084bc;}
.cui-select-view li:active{background:rgba(0,0,0,.05);}
.cui-select-view li.current:before,.cui-select-view li.current:after{position:absolute;content:"";background:#1084bc;height:3px;top:50%;border-radius:3px;}
.cui-select-view li.current:before{width:18px;right:10px;margin-top:-2px;-webkit-transform:rotate(-50deg);-moz-transform:rotate(-50deg);-ms-transform:rotate(-50deg);transform:rotate(-50deg);}
.cui-select-view li.current:after{width:9px;right:22px;margin-top:2px;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);}

.cui-roller{width:100%;height:90px;overflow:hidden;position:relative;display: -webkit-box;display: -moz-box;display: -ms-flexbox;display: -webkit-flex;display: flex;}
.cui-roller .ul-list{height:100%;text-align:center;font-size:1.1em;line-height:30px;-webkit-box-flex: 1;-moz-box-flex: 1;-webkit-flex: 1;-ms-flex: 1;flex: 1;}
.cui-mask{position:absolute;z-index:3;top: 0;left: 0;width: 100%;height: 100%;pointer-events: none;
background: linear-gradient(#fff 0,rgba(245,245,245,0) 52%,rgba(245,245,245,0) 48%,#fff 100%);
background: -webkit-gradient(linear,left bottom,left top,from(#fff),color-stop(0.52,rgba(245,245,245,0)),color-stop(0.48,rgba(245,245,245,0)),to(#fff));
background: -webkit-linear-gradient(#fff 0,rgba(245,245,245,0) 52%,rgba(245,245,245,0) 48%,#fff 100%);
background: -moz-linear-gradient(#fff 0,rgba(245,245,245,0) 52%,rgba(245,245,245,0) 48%,#fff 100%);
}
.cui-lines{width:100%;height:34px;position:absolute;top:50%;margin-top:-17px;border-top:#dfdfdf 1px solid;border-bottom:#dfdfdf 1px solid;}
.cui-roller-tips{color:#8d8d8d;padding:5px;text-align:center;}
.cui-roller-btns{background:#f6f5f5;padding:10px;text-align:center;}
.cui-roller-btns span{width:45%;display:inline-block;padding:10px 0;color:#fff;}
.cui-roller-btns span:active{opacity:.75;}
.cui-roller-btns .cui-btns-cancel{background:#a9a9a9;margin-right:5%;}
.cui-roller-btns .cui-btns-sure{background:#ff9913;}

</style>

<script id="others_zepto_10rc1" type="text/javascript" class="library" src="/js/sandbox/other/zepto.min.js"></script>
</head>
<body onselectstart="return false">
<div class="cui-pop-box">
<div class="cui-hd">
<div class="cui-text-center">
滚轮滚轮滚轮</div>
</div>
<div class="cui-bd">
<div id="ctripPage">
</div>
<p class="cui-roller-tips">
提示信息</p>
<div class="cui-roller-btns">
<span class="cui-btns-sure" id="set">设置值</span> <span class="cui-btns-sure" id="get">
获取值</span>
</div>
</div>
</div>
<script type="text/javascript">

// var myScroll = new iScroll('wl1');

(function () {
//!!!由于动画原因,获取值可能会出现不准确的情况,比如正在动画却已经取值了
//所以设置了一个冷却时间,在冷却时间的情况下设置值等操作不能进行
//为了保证唯一性,全部使用index作为索引算了
var ScrollRadio = function (opts) {
opts = opts || {};
//容器元素
this.wrapper = opts.wrapper || $(document);
this.body = [
'<div class="cui-roller">',
'<ul class="ul-list" style=" position: absolute; width: 100%; z-index: 2; " >',
'</ul>',
'<div class="cui-mask"></div>',
'<div class="cui-lines">&nbsp;</div>',
'</div>'
].join('');
this.body = $(this.body);

//真正拖动的元素(现在是ul)
this.dragEl = this.body.find('.ul-list');
//数据源
this.data = opts.data || [];
this._changed = opts.changed || null;
//当前选项索引默认选择2项
this.selectedIndex = 1;

//当前选项值
// this.key = '';
//当前选项显示的值
// this.value = '';

/*
定位实际需要用到的信息
暂时不考虑水平移动吧
*/
this.itemHeight = 0; //单个item高度
this.dragHeight = 0; //拖动元素高度
this.dragTop = 0; //拖动元素top
this.animateParam = [10, 6, 2, 1, 0, 0, 0, 0, 0, 0, 0]; //动画参数
this.timeGap = 0; //时间间隔
this.touchTime = 0; //开始时间
this.moveAble = false; //是否正在移动
this.moveState = 'up'; //移动状态,up right down left
this.oTop = 0; //拖动前的top值
this.curTop = 0; //当前容器top
this.mouseY = 0; //鼠标第一次点下时相对父容器的位置
this.cooling = false; //是否处于冷却时间

this.init();
};
ScrollRadio.prototype = {
constructor: ScrollRadio,
init: function () {
this.initItem();
this.wrapper.append(this.body);
this.initEventParam();
this.bindEvent();

return this;
},
//增加数据
initItem: function () {
var _tmp, _data, i, k;
for (var i in this.data) {
_data = this.data[i]
_tmp = $('<li>' + (_data.val == undefined ? i : _data.val) + '</li>');
_tmp.attr('data-index', i);
for (k in _data) {
_tmp.attr('data-' + k, _data[k]);
}
this.dragEl.append(_tmp);
}
},
//初始化事件需要用到的参数信息
initEventParam: function () {
var offset = this.dragEl.offset();
var itemOffset = this.dragEl.find('li').eq(0).offset();
this.itemHeight = itemOffset.height
this.dragTop = offset.top;
this.dragHeight = this.dragEl[0].scrollHeight;
var s = '';
},
bindEvent: function () {
var scope = this;
document.addEventListener("touchstart", function (e) {
scope.touchStart.call(scope, e);
}, false);
document.addEventListener("touchend", function (e) {
scope.touchEnd.call(scope, e);
}, false);
document.addEventListener("touchmove", function (e) {
scope.touchMove.call(scope, e);
}, false);
},
touchStart: function (e) {
if (this.cooling) return false; //冷却时间不能开始

//需要判断是否是拉取元素,此处需要递归验证,这里暂时不管
//!!!!!!!!此处不严谨
var el = $(e.srcElement).parent(), pos;
if (el.hasClass('ul-list')) {
this.moveAble = true;

this.touchTime = e.timeStamp;
//获取鼠标信息
pos = this.getMousePos(e.changedTouches[0]);
//注意,此处是相对位置,注意该处还与动画有关,所以高度必须动态计算
//可以设置一个冷却时间参数,但想想还是算了
//最后还是使用了冷却时间
// var top = parseFloat(this.dragEl.css('top')) || 0;
// this.mouseY = pos.top - top;
this.mouseY = pos.top - this.curTop;
this.moveAble = true;
}
},
touchMove: function (e) {
if (!this.moveAble) return false;
var pos = this.getMousePos(e.changedTouches[0]);
//先获取相对容器的位置,在将两个鼠标位置相减
this.curTop = pos.top - this.mouseY;
this.dragEl.css('top', this.curTop + 'px');
e.preventDefault();
},
touchEnd: function (e) {
if (!this.moveAble) return false;
this.cooling = true; //开启冷却时间

//时间间隔
var scope = this;
this.timeGap = e.timeStamp - this.touchTime;
var flag = this.oTop <= this.curTop ? 1 : -1; //判断是向上还是向下滚动
var flag2 = this.curTop > 0 ? 1 : -1; //这个会影响后面的计算结果
this.moveState = flag > 0 ? 'up' : 'down';
var ih = parseFloat(this.itemHeight);
var ih1 = ih / 2;

var top = Math.abs(this.curTop);
var mod = top % ih;
top = (parseInt(top / ih) * ih + (mod > ih1 ? ih : 0)) * flag2;

var step = parseInt(this.timeGap / 50);
step = step > 0 ? step : 0;
var speed = this.animateParam[step] || 0;
var increment = speed * ih * flag
top += increment;
//!!!此处动画可能导致数据不同步,后期改造需要加入冷却时间
if (this.oTop != this.curTop) {
this.dragEl.animate({
top: top + 'px'
}, 100 + (speed * 20), 'ease-out', function () {
var _top = top, t = false; ;
if (top > ih) {
_top = ih;
t = true;
}
if (top < 0 && (top + scope.dragHeight < ih * 2)) {
t = true;
_top = (scope.dragHeight - ih * 2) * (-1);
}
if (t) {
scope.dragEl.animate({
top: _top + 'px'
}, 10, 'ease-in-out', function () {
scope.oTop = _top;
scope.curTop = _top;
scope.cooling = false; //关闭冷却时间
scope.onTouchEnd();
});
} else {
scope.cooling = false; //关闭冷却时间
scope.oTop = top;
scope.curTop = top;
scope.onTouchEnd();
}
});
} else {
this.cooling = false; //关闭冷却时间
this.onTouchEnd();
}
this.moveAble = false;
},
onTouchEnd: function () {
var i = parseInt((this.curTop - this.itemHeight) / parseFloat(this.itemHeight));
this.selectedIndex = Math.abs(i);
var secItem = this.data[this.selectedIndex];
//触发变化事件
var changed = this._changed;
if (changed && typeof changed == 'function') {
changed.call(this, secItem);
}
console.log(this.selectedIndex, secItem);
},
setKey: function (k) { },
setVal: function (v) { },
setIndex: function (i) {
var i = parseInt(i);
if (i >= this.data.length || i < 0) return false;

this.selectedIndex = i;
this.curTop = (i * this.itemHeight * (-1) + this.itemHeight);
this.dragEl.css('top', this.curTop + 'px');
},
getSelected: function () {
return this.data[this.selectedIndex];
},
getByKey: function (k) { },
getByVal: function (v) { },
getByIndex: function (i) { },
//获取鼠标信息
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
};
}
};
window.ScrollRadio = ScrollRadio;

})();

var data = [];
for (var i = 0; i < 20; i++) {
var temp = { val: '选项-' + i };
data.push(temp);
}

var myScroll = new ScrollRadio({
wrapper: $('#ctripPage'),
data: data
});

$('#set').click(function () {
var i = prompt("请输入索引值")
if (i != null && i != "") {
myScroll.setIndex(i);
}
});

$('#get').click(function () {
var d = myScroll.getSelected();
var str = '';
for (var k in d) {
str += k + ': ' + d[k] + ',';
}
alert(str);
});

var s = '';

</script>
</body>
</html>

我们这里简单看看代码:

  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

<!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>
<style type="text/css">

/*reset*/
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,button,input,select,textarea{font:normal 14px/1.5 Tahoma,"Lucida Grande",Verdana,"Microsoft YaHei",hei;}
article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section,iframe{display:block;}
h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:500;}
address,cite,dfn,em,var,i{font-style:normal;font-weight:normal;font-family:arial;}
ul,ol{list-style:none}
a{color:#000;text-decoration:none}
a:hover{text-decoration:underline;-webkit-transition:color .2s linear;-moz-transition:color .2s linear;-ms-transition:color .2s linear;-o-transition:color .2s linear;transition:color .2s linear;}
fieldset,img,button,input{border:0}
button,input,select,textarea{font-size:100%}
table{border-collapse:collapse;border-spacing:0}
input[type="button"],input[type="submit"]{-webkit-appearance:none;}
body{min-width:320px; background:#f5f5f5;overflow-x:hidden;-webkit-text-size-adjust:none;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-font-smoothing:subpixel-antialiased;-moz-user-select:none;color:#000;}:focus{outline:none;}.clearfix:after{clear:both;content:'.';display:block;height:0;visibility:hidden;line-height:0;}.clearfix{*zoom:1;}.fl{float:left;}.fr{float:right;}.clear{clear:both;}.overflow{overflow:hidden;}.ellips{white-space:nowrap; overflow:hidden; text-overflow:ellipsis;}.ellips_line2,.ellips_line3{display: -webkit-box;-webkit-box-orient: vertical;overflow: hidden;}.ellips_line2{-webkit-line-clamp:2;}.ellips_line3{-webkit-line-clamp:3;}.pos_rel{position:relative;}
html,body,.h100{ height:100%; }
dfn{color:#ff9913; font-size:12px; font-weight:normal;}
/*-----------------------------------------common--------------------*/
.i,.i_bef:before,.i_aft:after,.carlist li:before,.ok_crt:after{background:url(../img/style1/v2_bgs.png) no-repeat; background-size:175px 125px;}
.i_bef:before,.i_aft:after,.carlist li:before{position: absolute;content: "";}
.abs_size{-moz-box-sizing:border-box; -webkit-box-sizing:border-box; box-sizing:border-box;}
.opacity{opacity:0.7; filter:alpha(opacity=70);}

/*active*/
.list_st_border li:active,.p_hinttxt:active,.search_cancel:active,.citylist dd:active{ background:#f8f8f8;}
.btn_yellow:active{background:#eb840f;}

/*layout*/
.wrap{margin-top:48px;}
.wrap_pb{margin-top:48px; padding-bottom:45px;}
.p10{padding:10px;}
.hm{text-align:center;}
.break_all{word-break:break-all;}
.fix_b{position:fixed; bottom:0; left:0;z-index:9999;}

/*font bg color size*/
h1{font:normal 1.286em/1.5 "";}/*18px*/
h2{font:normal 1.143em/1.5 "";}/*16px*/
h3{font:600 1em/1.5 "";}/*14px*/
.price{margin-left:5px;}
.price i{margin-left:2px; font-size:1.286em;}
.greyfont{color:#686868;}
.greyfont1{color:#909090;}
.bggrey{height:100%; background:#f5f5f5;}

/*header*/
header{position:fixed; top:0; left:0; z-index:9999; width:100%; height:48px;background-color:#1491c5;}
.returnico{position:absolute; left:0; top:0; width:68px; height:48px; background-color:#15a4d5;}
.returnico:before{left:25px;top:16px;width:12px;height:20px; background-position:0 0;}
.icon_phone,.icon_home{width:42px;height:100%;top:0;position:relative;float:right;}
.icon_phone:before{width:11px;height:20px;top:16px;right:12px;background-position:-115px -65px;}
.icon_home:before{width:20px;height:19px;top:16px;right:10px;background-position:-83px -66px;}
header i:active{opacity:0.7; filter:alpha(opacity=70);}
header h1{position:absolute; width:100%;line-height:48px; text-align:center; letter-spacing:2px; color: #fff;}
.header_r,.header_rs{position:absolute;z-index:9; top:0; right:0; line-height:48px; padding:0 15px; font-size:18px; background:#15a4d5; color:#fff;}
.header_rs{padding:0 5px; font-size:12px; }

/*background-position*/
.select_n:before{width:20px;height:20px;background-position:-18px 0;}
.select_n.current:before{background-position:-44px 0;}

/*searchbox*/
.search_wrap{overflow:hidden; padding:10px; background:#dfeaf1;}
.search_box{position:relative; float:left; width:100%;}
.search_input{width:100%; padding:0 10px 0 28px; line-height:30px; background-color:#fff; border-radius:4px; color:#ccc;}
.fdj:before,.fdj:after,.search_box:before,.search_box:after{position:absolute; content:""; z-index:9;}
.fdj:before,.search_box:before{left:5px; top:7px; width:12px; height:12px; border:1px solid #bcbcbc; border-radius:12px;}
.fdj:after,.search_box:after{left:19px; top:17px; width:1px; height:8px; background:#bcbcbc;}
.search_cancel{ display:none;float:left; width:20%; line-height:30px; text-align:center; font-size:16px;color:#1491c5; background:none; border:none;}
.close_icon{display:none; position:absolute; z-index:10; top:8px; right:4px; width:16px; height:16px; border-radius:30px; background:#b1b1b1;}
.close_icon:before,.close_icon:after{position:absolute; content:""; top:4px; left:7px; width:2px; height:8px; background:#fff; }
.close_icon:before{-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg);}
.fdj:after,.search_box:after,.close_icon:after{-webkit-transform:rotate(-45deg);-moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg);}
.search_focus .search_box{width:80%;}
.search_focus .close_icon, .search_focus .search_cancel{display:block;}
.search_input:focus{color:#000;}

/*tab*/
.tab{ background-color:#f8f8f8; border-bottom:1px solid #dfdfdf;}
.tab li{float:left; width:50%; height:39px; line-height:39px; text-align:center; border-right:1px solid #dfdfdf;}
.tab li:last-child{border-right:none;}
.tab li.tabcrt{ background-color:#dfdfdf;}

.tab_b{ background-color:#f5f5f5; border-bottom:1px solid #c1c1c1;}
.tab_b li{float:left; width:50%; height:39px; line-height:39px; text-align:center;}
.tab_b li:last-child{border-right:none;}
.tab_b li.tabcrt{color:#1491c5; border-bottom:4px solid #1491c5;}

/*list*/
.list_st_border{ background:#fff; border-bottom:none; border:1px solid #cfcfcf; }
.list_st_border li{position:relative;padding:0 10px; line-height:42px; border-bottom:1px solid #cfcfcf;}
.list_st_border li:last-child{border-bottom:none;}

.list_sli{padding:10px; overflow:hidden; border-bottom:1px solid #cfcfcf; background:#fff;}
.list_sli .list_sunit{float:left;}

.citylist{color:#000;}
.citylist dt,.citylist dd{padding-left:10px;border-bottom:1px solid #e2e2e2;}
.citylist dt{line-height:25px; background-color:#eaeaea;}
.citylist dd{position:relative; font-size:16px; line-height:43px; background-color:#fff;}
.citylist .ok_crt{color:#1491c5;}
.citylist .ok_crt:after{position:absolute;content:""; right:10px; top:50%; margin-top:-6px; width:12px; height:13px; background-position:-110px 0;}

/*arr*/
.li_arr_r{position:relative;}
.arr_r{position:absolute;right:0px; top:50%; width:30px; height:30px; margin-top:-15px; }
.arr_r:before,.arr_r:after,.li_arr_r:before,.li_arr_r:after{position: absolute; left:15px; content: "";width:2px; height:7px; background-color:#909090;}
.arr_r:before,.li_arr_r:before{top:10px;-webkit-transform:rotate(-45deg); -moz-transform:rotate(-45deg);-ms-transform:rotate(-45deg);-o-transform:rotate(-45deg);transform:rotate(-45deg);}
.arr_r:after,.li_arr_r:after{top:15px;-webkit-transform:rotate(45deg); -moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg);}
.li_arr_r:before,.li_arr_r:after{left:auto; right:10px; top:50%; margin-top:-5px;}
.li_arr_r:after{margin-top:0;}

/*p*/
.p_grey{margin:10px 5px; font-size:13px; color:#989898;}
.p_grey_center{ text-align:center; margin:20px 5px; font-size:13px; color:#989898;}
.p_hinttxt{padding:20px 10px; text-align:center; font-size:16px; color:#1491c5;}

/*btn*/
.btn_yellow,.btn_del{width:100%; height:44px; line-height:42px; padding:0 10px; color:#fff;}
.btn_yellow{background:#ff9913;}
.btn_del{background:#ca4345;text-align:center;font-size:1.2em;}

.btn_pay{padding:0 20px;height:44px;float:right;background:#ff7d13;color:#fff;}
.btn_pay:active{background:#ff7300;}
.room_num {position:absolute; right:10px; width:100px; height:30px; line-height:30px;color:#000;background-color: #fff; text-align:center;border:#bfbfbf 1px solid;}
.room_num i{position:absolute; width:30px; height:30px; text-align:center; font:normal 2em/28px "Arial";}
.room_num i:first-child{left:0;background:#f4f4f4;color:#d9d9d9;font:normal 2.8em/25px "Arial";}
.room_num i:last-child{right:0;background:#06a2d0;color:#fff;}

/*日历*/
.cui_cldwrap{color:#000;}
.cui_cldweek{height:25px; overflow:hidden;font:normal 12px/24px "verdana";border-bottom:1px solid #c8c8c8;}
.cui_cldweek li{float:left; width:14%; text-align:center; }
.cui_cldweek li:first-child,.cui_cldweek li:last-child{width:15%; color:#acacac;}
.cui_cldmonth{height:35px;text-align:center;font:normal 16px/35px "verdana"; background:#fff;}
.cui_cldunit{margin-bottom:20px;}
.cui_cld_daybox{overflow:hidden; background:#fff;}
.cui_cld_daybox li{float:left; width:14%; height:45px; padding:4px 0; font:normal 12px/45px "verdana"; overflow:hidden; text-align:center;}
.cui_cld_daybox li:nth-child(7n),.cui_cld_daybox li:nth-child(7n+1){width:15%;}
.cui_cld_daypass{ background:#f7f7f7;}
.cui_cld_daycrt{background:#06a2d0; color:#fff;}
.cui_cld_dayfuture{background:#fff;}
.cui_cld_day_nocrtmonth{ visibility:hidden;}
.cui_cld_day_havetxt em{display:block; line-height:25px;}
.cui_cld_day_havetxt i{display:block;line-height:15px;}
.cui_cld_day_hint{color:#06a2d0;}
/*全局XXXX*/

/*弹出蓝色框*/
.cui-pop-box{background:#fff;}
.cui-text-center{text-align:center;}
.cui-pop-box .cui-hd{height:40px;line-height:40px;font-size:1.2em;color:#fff;background:#1491c5;padding:0 10px;position:relative;}
.cui-pop-box .cui-hd .cui-close{position:absolute;top:10px; right:10px; width:18px; height:18px;line-height:18px;border-radius:50%; background:#fff;color:#1491c5;text-align:center;font-weight:bold;}
.cui-error{width:150px;margin:auto;border-radius:5px;background:rgba(73,73,73,1);padding:10px;color:#fff;font-weight:700;text-align:center;word-break:break-all;}
.cui-select-view li{border-bottom:#dfdfdf 1px solid;padding:.8em 2em .8em .8em;white-space:nowrap; overflow:hidden; text-overflow:ellipsis;position:relative;}
.cui-select-view li.current{color:#1084bc;}
.cui-select-view li:active{background:rgba(0,0,0,.05);}
.cui-select-view li.current:before,.cui-select-view li.current:after{position:absolute;content:"";background:#1084bc;height:3px;top:50%;border-radius:3px;}
.cui-select-view li.current:before{width:18px;right:10px;margin-top:-2px;-webkit-transform:rotate(-50deg);-moz-transform:rotate(-50deg);-ms-transform:rotate(-50deg);transform:rotate(-50deg);}
.cui-select-view li.current:after{width:9px;right:22px;margin-top:2px;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);transform:rotate(45deg);}

.cui-roller{width:100%;height:90px;overflow:hidden;position:relative;display: -webkit-box;display: -moz-box;display: -ms-flexbox;display: -webkit-flex;display: flex;}
.cui-roller .ul-list{height:100%;text-align:center;font-size:1.1em;line-height:30px;-webkit-box-flex: 1;-moz-box-flex: 1;-webkit-flex: 1;-ms-flex: 1;flex: 1;}
.cui-mask{position:absolute;z-index:3;top: 0;left: 0;width: 100%;height: 100%;pointer-events: none;
background: linear-gradient(#fff 0,rgba(245,245,245,0) 52%,rgba(245,245,245,0) 48%,#fff 100%);
background: -webkit-gradient(linear,left bottom,left top,from(#fff),color-stop(0.52,rgba(245,245,245,0)),color-stop(0.48,rgba(245,245,245,0)),to(#fff));
background: -webkit-linear-gradient(#fff 0,rgba(245,245,245,0) 52%,rgba(245,245,245,0) 48%,#fff 100%);
background: -moz-linear-gradient(#fff 0,rgba(245,245,245,0) 52%,rgba(245,245,245,0) 48%,#fff 100%);
}
.cui-lines{width:100%;height:34px;position:absolute;top:50%;margin-top:-17px;border-top:#dfdfdf 1px solid;border-bottom:#dfdfdf 1px solid;}
.cui-roller-tips{color:#8d8d8d;padding:5px;text-align:center;}
.cui-roller-btns{background:#f6f5f5;padding:10px;text-align:center;}
.cui-roller-btns span{width:45%;display:inline-block;padding:10px 0;color:#fff;}
.cui-roller-btns span:active{opacity:.75;}
.cui-roller-btns .cui-btns-cancel{background:#a9a9a9;margin-right:5%;}
.cui-roller-btns .cui-btns-sure{background:#ff9913;}

</style>
<style type="text/css">
.c { position: absolute; top: 0; }
#y { left: 0; width: 30%; }
#m { left: 30%; right: 40%; }
#d { right: 0; width: 40%; }
</style>
<script src="../zepto.js" type="text/javascript"></script>
</head>
<body onselectstart="return false">
<div class="cui-pop-box">
<div class="cui-hd">
<div class="cui-text-center">
我是日历控件</div>
</div>
<div class="cui-bd">
<div style="height: 90px; position: relative;">
<div id="y" class="c">
</div>
<div id="m" class="c">
</div>
<div id="d" class="c">
</div>
</div>
<p class="cui-roller-tips">
点击获取日期吧!</p>
<div class="cui-roller-btns">
<span class="cui-btns-cancel" id="set">取消</span> <span class="cui-btns-sure" id="get">
获取日期</span>
</div>
</div>
</div>

<script type="text/javascript">

// var myScroll = new iScroll('wl1');

(function () {
//!!!由于动画原因,获取值可能会出现不准确的情况,比如正在动画却已经取值了
//所以设置了一个冷却时间,在冷却时间的情况下设置值等操作不能进行
//为了保证唯一性,全部使用index作为索引算了
var ScrollRadio = function (opts) {
opts = opts || {};
//容器元素
this.wrapper = opts.wrapper || $(document);
var id = new Date().getTime() + Math.random() + 'id';
this.body = [
'<div class="cui-roller">',
'<ul class="ul-list" style=" position: absolute; width: 100%; z-index: 2; " id="' + id + '" >',
'</ul>',
'<div class="cui-mask"></div>',
'<div class="cui-lines">&nbsp;</div>',
'</div>'
].join('');
this.body = $(this.body);

//真正拖动的元素(现在是ul)
this.dragEl = this.body.find('.ul-list');
//数据源
this.data = opts.data || [];
this._changed = opts.changed || null;
//当前选项索引默认选择2项
this.selectedIndex = 1;

//当前选项值
// this.key = '';
//当前选项显示的值
// this.value = '';

/*
定位实际需要用到的信息
暂时不考虑水平移动吧
*/
this.itemHeight = 0; //单个item高度
this.dragHeight = 0; //拖动元素高度
this.dragTop = 0; //拖动元素top
this.animateParam = [10, 6, 2, 1, 0, 0, 0, 0, 0, 0, 0]; //动画参数
this.timeGap = 0; //时间间隔
this.touchTime = 0; //开始时间
this.moveAble = false; //是否正在移动
this.moveState = 'up'; //移动状态,up right down left
this.oTop = 0; //拖动前的top值
this.curTop = 0; //当前容器top
this.mouseY = 0; //鼠标第一次点下时相对父容器的位置
this.cooling = false; //是否处于冷却时间

this.init();
};
ScrollRadio.prototype = {
constructor: ScrollRadio,
init: function () {
this.initItem();
this.wrapper.append(this.body);
this.initEventParam();
this.bindEvent();
},
//增加数据
initItem: function () {
var _tmp, _data, i, k;
for (var i in this.data) {
_data = this.data[i]
_tmp = $('<li>' + (_data.val == undefined ? i : _data.val) + '</li>');
_tmp.attr('data-index', i);
for (k in _data) {
_tmp.attr('data-' + k, _data[k]);
}
this.dragEl.append(_tmp);
}
},
//初始化事件需要用到的参数信息
initEventParam: function () {
var offset = this.dragEl.offset();
var itemOffset = this.dragEl.find('li').eq(0).offset();
this.itemHeight = itemOffset.height
this.dragTop = offset.top;
this.dragHeight = this.dragEl[0].scrollHeight;
var s = '';
},
bindEvent: function () {
var scope = this;
this.dragEl[0].addEventListener("touchstart", function (e) {
scope.touchStart.call(scope, e);
}, false);
this.dragEl[0].addEventListener("touchend", function (e) {
scope.touchEnd.call(scope, e);
}, false);
this.dragEl[0].addEventListener("touchmove", function (e) {
scope.touchMove.call(scope, e);
}, false);
},
touchStart: function (e) {
if (this.cooling) return false; //冷却时间不能开始

//需要判断是否是拉取元素,此处需要递归验证,这里暂时不管
//!!!!!!!!此处不严谨
var el = $(e.srcElement).parent(), pos;
if (el.hasClass('ul-list')) {
this.moveAble = true;

this.touchTime = e.timeStamp;
//获取鼠标信息
pos = this.getMousePos(e.changedTouches[0]);
//注意,此处是相对位置,注意该处还与动画有关,所以高度必须动态计算
//可以设置一个冷却时间参数,但想想还是算了
//最后还是使用了冷却时间
// var top = parseFloat(this.dragEl.css('top')) || 0;
// this.mouseY = pos.top - top;
this.mouseY = pos.top - this.curTop;
this.moveAble = true;
}
},
touchMove: function (e) {
if (!this.moveAble) return false;
var pos = this.getMousePos(e.changedTouches[0]);
//先获取相对容器的位置,在将两个鼠标位置相减
this.curTop = pos.top - this.mouseY;
this.dragEl.css('top', this.curTop + 'px');
e.preventDefault();
},
touchEnd: function (e) {
if (!this.moveAble) return false;
this.cooling = true; //开启冷却时间

//时间间隔
var scope = this;
this.timeGap = e.timeStamp - this.touchTime;
var flag = this.oTop <= this.curTop ? 1 : -1; //判断是向上还是向下滚动
var flag2 = this.curTop > 0 ? 1 : -1; //这个会影响后面的计算结果
this.moveState = flag > 0 ? 'up' : 'down';
var ih = parseFloat(this.itemHeight);
var ih1 = ih / 2;

var top = Math.abs(this.curTop);
var mod = top % ih;
top = (parseInt(top / ih) * ih + (mod > ih1 ? ih : 0)) * flag2;

var step = parseInt(this.timeGap / 50);
step = step > 0 ? step : 0;
var speed = this.animateParam[step] || 0;
var increment = speed * ih * flag
top += increment;
//!!!此处动画可能导致数据不同步,后期改造需要加入冷却时间
if (this.oTop != this.curTop) {
this.dragEl.animate({
top: top + 'px'
}, 100 + (speed * 20), 'ease-out', function () {
var _top = top, t = false; ;
if (top > ih) {
_top = ih;
t = true;
}
if (top < 0 && (top + scope.dragHeight < ih * 2)) {
t = true;
_top = (scope.dragHeight - ih * 2) * (-1);
}
if (t) {
scope.dragEl.animate({
top: _top + 'px'
}, 10, 'ease-in-out', function () {
scope.oTop = _top;
scope.curTop = _top;
scope.cooling = false; //关闭冷却时间
scope.onTouchEnd();
});
} else {
scope.cooling = false; //关闭冷却时间
scope.oTop = top;
scope.curTop = top;
scope.onTouchEnd();
}
});
} else {
this.cooling = false; //关闭冷却时间
this.onTouchEnd();
}
this.moveAble = false;
},
onTouchEnd: function () {
var i = parseInt((this.curTop - this.itemHeight) / parseFloat(this.itemHeight));
this.selectedIndex = Math.abs(i);
var secItem = this.data[this.selectedIndex];
//触发变化事件
var changed = this._changed;
if (changed && typeof changed == 'function') {
changed.call(this, secItem);
}
console.log(this.selectedIndex, secItem);
},
setKey: function (k) { },
setVal: function (v) { },
setIndex: function (i) {
var i = parseInt(i);
if (i >= this.data.length || i < 0) return false;

this.selectedIndex = i;
this.curTop = (i * this.itemHeight * (-1) + this.itemHeight);
this.dragEl.css('top', this.curTop + 'px');
},
getSelected: function () {
return this.data[this.selectedIndex];
},
getByKey: function (k) { },
getByVal: function (v) { },
getByIndex: function (i) { },
//获取鼠标信息
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
};
}
};
window.ScrollRadio = ScrollRadio;

})();

var yy = [];
for (var i = 0; i < 150; i++) {
var temp = { val: 1900 + i };
yy.push(temp);
}

var mm = [];
for (var i = 0; i < 12; i++) {
var temp = { val: i > 10 ? i : (0 + '' + i) };
mm.push(temp);
}

var dd = [];
for (var i = 0; i < 31; i++) {
var temp = { val: i > 10 ? i : (0 + '' + i) };
dd.push(temp);
}

var y = new ScrollRadio({
wrapper: $('#y'),
data: yy
});
var m = new ScrollRadio({
wrapper: $('#m'),
data: mm
});
var d = new ScrollRadio({
wrapper: $('#d'),
data: dd
});

$('#get').click(function () {
alert(y.getSelected().val + '-' + m.getSelected().val + '-' + d.getSelected().val);
});

var s = '';

</script>
</body>
</html>

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

结语

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

 
 
分类: 移动开发

日历插件(beta)的更多相关文章

  1. 仿iphone日历插件(beta)

    前言 小伙伴们好,很久不见了.最近工作进入正常期了,所以慢慢的悠闲的时间久没有了,所以不能每天水一篇了. 最近也在听师傅(http://home.cnblogs.com/u/aaronjs/)的教导开 ...

  2. 被逼着写的jquery工作日管理日历插件

    因为工作原因,在我刚进入新公司之后,立马要求让我做一个jquery的插件demo.我的天,我面试的可是.net工程师啊.虽然以前接触过js,jquery,但也只是接触过一丢丢啊,没办法,只好硬着头皮上 ...

  3. 日历插件FullCalendar应用:(二)数据增删改

    接上一篇 日历插件FullCalendar应用:(一)数据展现. 这一篇主要讲使用fullcalendar插件如何做数据的增删改,用到了art.dialog web对话框组件,上一篇用到的webFor ...

  4. 一款基于jQuery日历插件的开发过程

    这个插件的设置选项,所有样式都设置成自定义,提供回调函数,方便在外部进行扩展 css设置是可变的  也就是说  日历的样式是定制的: /******************************** ...

  5. 一款简洁大气的jquery日期日历插件

    本jquery插件名为manhuaDate,暂时只支持jquery 1.9.0以下版本,比如jquery-1.8.3.min.js 查看效果网址:http://keleyi.com/a/bjad/em ...

  6. 【UI插件】简单的日历插件(下)—— 学习MVC思想

    前言 我们上次写了一个简单的日历插件,但是只是一个半成品,而且做完后发现一些问题,于是我们今天尝试来解决这些问题 PS:距离上次貌似很久了 上次,我们大概遇到哪些问题呢: ① 既然想做一套UI库,那么 ...

  7. 【UI插件】开发一个简单日历插件(上)

    前言 最近开始整理我们的单页应用框架了,虽然可能比不上MVVM模式的开发效率,也可能没有Backbone框架模块清晰,但是好歹也是自己开发出来 而且也用于了这么多频道的东西,如果没有总结,没有整理,没 ...

  8. Jquery中的日历插件

    这个插件很简单:只需要在HTML代码中引入插件如下,CLASS名和click事件函数固定! <!doctype html> <html lang="en"> ...

  9. js日历插件 中文、英文日历

    日历插件 来源网站:http://www.cnblogs.com/yank/archive/2008/08/14/1267746.html 六款英文日历 http://www.bobd.cn/desi ...

随机推荐

  1. Linux_修改创建文件夹时默认权限(修改为能上传)

    1:查看当前权限 umask 0022 意思就是权限为:777-022 =755 读 2:临时修改 umask 020 020 意思为:777-020=757 读写上传 3:永久修改 回到根目录 cd ...

  2. HDU 1243 畅通project 并査集

    Total Submission(s): 31033    Accepted Submission(s): 16313 Problem Description 某省调查城镇交通状况,得到现有城镇道路统 ...

  3. Android开发之控制Toast的开启与关闭

    开发这个程序之前先解释一下,为什么Toast信息提示框在显示一定时间后会自己主动消失?由于在Android系统中有一个Toast队列,系统会依次从这个队列中取出一个Toast,并显示它.在显示了指定时 ...

  4. Cocos2d-x之MenuItem

    ***************************************转载请注明出处:http://blog.csdn.net/lttree************************** ...

  5. Microsoft dotnetConf 2015

    Microsoft dotnetConf 2015 一些整理   dotnetConf 2015 直播计划: 第一天 (10 个主讲) - .NET Product Teams 主讲人为 .NET 产 ...

  6. Java用ZIP格式压缩和解压缩文件

    转载:java jdk实例宝典 感觉讲的非常好就转载在这保存! java.util.zip包实现了Zip格式相关的类库,使用格式zip格式压缩和解压缩文件的时候,须要导入该包. 使用zipoutput ...

  7. C++内存泄露的有效预防方法:谁使用,谁删除 (1.2)

    内存泄露就是new出来的东西没有delete,我们能够这样:创建动态对象的人虽然使用new来创建对象:使用此对象的人负责释放此内存块. 比如:我和他人共享一个消息队列,他人将消息(new出来的对象)放 ...

  8. MySql状态查看方法 MySql如何查看连接数和状态?

    原文:MySql状态查看方法 MySql如何查看连接数和状态? 如果是root帐号,你能看到所有用户的当前连接.如果是其它普通帐号,只能看到自己占用的连接 怎么进入mysql命令行呢? mysql的安 ...

  9. Oracle SQL in 超过1000 的解决方案

    处理 Oracle SQL in 超过1000 的解决方案 处理oracle sql 语句in子句中(where id in (1, 2, ..., 1000, 1001)),如果子句中超过1000项 ...

  10. J2EE

    随笔分类 - J2EE   关于SpringMVC Json使用 摘要: 很简单的一个东西,这里就不做过多介绍了,写一个小Demo,随手记录下.首先,在搭好SpringMVC工程环境之后,如果想用Sp ...