仿iphone日历插件(beta)

前言

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

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

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

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

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

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

关于工作

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

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

② 模仿一个iphone的日历控件

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

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

iphone的感觉

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

日历插件(beta)-LMLPHP

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

日历插件(beta)-LMLPHP

总体思路

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

日历插件(beta)-LMLPHP

关于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>

我们这里简单看看代码:

日历插件(beta)-LMLPHP
  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 })();
日历插件(beta)-LMLPHP

以上代码基本框架都出来了,也有注释,有兴趣的朋友自己看看,我们现在进行最后一步了,将日历搞上去,老夫来不起了。。。

日历插件:)

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应该很多的。

 
 
分类: 移动开发
05-08 08:41