前一篇文章刚给大家分享了滑动组件(http://blog.csdn.net/sq_zhuyi/article/details/79103683),本篇文章就正好利用到这个滑动组件,在日历控件中实现选择时间的模块。同样,为了便于入题,我们先看效果图:
这是一个在租车项目中选择取车时间和还车时间的日历,其实同样的场景被用于很多旅游项目,用来选择出发、返程时间。
上代码,CSS模块:
.acal {
position: absolute;
left: 0;
right: 0;
top: 44px;
z-index: 100;
}
.acal .head {
position: fixed;
left: 0;
right: 0;
top: 44px;
z-index: 12;
height: 44px;
padding: 0 25px;
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
background-color: #fff;
text-align: center;
}
.acal .head .dt {
line-height: 44px;
font-size: 18px;
}
.acal .head .day {
width: 3.5em;
line-height: 26px;
border: 1px solid #f00;
border-radius: 8px;
margin-top: 8px;
}
.acal .week {
position: fixed;
left: 0;
right: 0;
top: 88px;
z-index: 11;
line-height: 32px;
text-align: center;
background-color: #fff;
border-bottom: 1px solid #eee;
box-shadow: 0 2px 20px rgba(0,0,0,0.2);
}
.acal .week>i {
width: 14.28%;
}
.acal .calbody {
padding: 80px 0 200px 0;
background-color: #f9f9f9;
}
.acal .month {
position: relative;
}
.acal .month>h2 {
text-align: center;
line-height: 295px;
font-size: 80px;
color: #dedede;
font-weight: normal;
}
.acal .month .days {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
.acal .month .days>i {
float: left;
width: 59px;
height: 39px;
margin: 10px 0;
text-align: center;
font-size: 20px;
position: relative;
}
.acal .month .days em {
width: 39px;
line-height: 39px;
border-radius: 50%;
position: absolute;
left: 10px;
top: 0;
z-index: 2;
}
.acal .month .days>i.disabled {
color: #999;
}
.acal .month .days>i.hover {
color: #fff;
}
.acal .month .days>i.hover>em {
background-color: #f00;
}
.acal .month .days>i.hover::before {
content: "";
position: absolute;
top: 0;
bottom: 0;
z-index: 1;
background-color: #FFD2CF;
}
.acal .month .days>i.start::before {
left: 30px;
right: 0;
}
.acal .month .days>i.end::before {
left: 0;
right: 30px;
}
.acal .month .days>i.active {
background-color: #FFD2CF;
}
.acal .time {
position: fixed;
left: 0;
right: 0;
bottom: 60px;
z-index: 10;
border-top: 1px solid #eee;
background-color: #fff;
box-shadow: 0 -2px 20px rgba(0,0,0,0.2);
}
.acal .time .tit {
float: left;
font-size: 16px;
width: 100px;
text-indent: 14px;
line-height: 56px;
}
.acal .time .slide {
float: left;
width: 300px;
height: 2px;
margin-top: 26px;
background-color: #ddd;
}
.acal .time .value {
float: left;
height: 2px;
background-color: #f00;
position: relative;
}
.acal .time .value>em {
position: absolute;
right: -30px;
top: -10px;
line-height: 22px;
padding: 0 6px;
border: 1px solid #f00;
background-color: #fff;
border-radius: 20px;
}
日期模块:
/**
* 日历
* @param {object} options
* @param {number} options.minday 最少提前几天预定
* @param {number} options.months 显示几个月
* @param {date} options.start 默认开始时间
* @param {date} options.end 默认结束时间
* @param {function} options.callback 选择时间后的回调函数
*/
function Calendar(options) {
this.options = options;
this.options.daysel = '.acal .days>i';
this.init();
}
Calendar.prototype.init = function () {
var ins = this;
if($('.acal').length==0){
ins._createDOM();
ins._bindEvent();
}
// 初始化日期-
var start = ins.options.start;
var m1 = $('.acal [data-m="{0}-{1}"]'.format(start.getFullYear(), start.getMonth()));
var d1 = m1.next().find('[data-d="{0}"]'.format(start.getDate()));
d1.trigger('click');
var end = ins.options.end;
var m2 = $('.acal [data-m="{0}-{1}"]'.format(end.getFullYear(), end.getMonth()));
var d2 = m2.next().find('[data-d="{0}"]'.format(end.getDate()));
d2.trigger('click');
// 初始化时间-
var arr = $('.acal .time em');
var t1 = start.date(5).split(' ')[1];
arr.eq(0).html(t1).parent().data('value', t1);
var t2 = end.date(5).split(' ')[1];
arr.eq(1).html(t1).parent().data('value', t2);
var tt = [];
for (var i = 0; i < 24; i++) {
tt.push(i + ':00');
tt.push(i + ':30');
}
new Slider({
bar: $('.acal .slide').eq(0),
btn: $('.acal .slide em').eq(0),
data: tt,
width: 300,
callback: ins.setValue.bind(ins)
}).init();
new Slider({
bar: $('.acal .slide').eq(1),
btn: $('.acal .slide em').eq(1),
data: tt,
width: 300,
callback: ins.setValue.bind(ins)
}).init();
ins.setValue();
};
Calendar.prototype.hide = function () {
$('.acal').css('display', 'none');
}
Calendar.prototype.show = function () {
$('.acal').css('display', 'block');
}
Calendar.prototype._bindEvent = function () {
var ins = this;
var options = this.options;
$(options.daysel).on('click', function () {
var $t = $(this);
if ($t.hasClass('disabled')) return;
var len = $(options.daysel + '.hover').length;
$t.addClass('hover');
if (len == 1) {
ins._resetDate();
} else if (len == 2) {
$(options.daysel).removeClass('hover active start end');
$t.addClass('hover');
}
});
$('.acal .btn-red').on('click', function(){
ins.hide();
if(ins.options.callback){
ins.options.callback(options.start, options.end);
}
});
}
Calendar.prototype._createDOM = function () {
var ss = '<div class="acal" style="display:none;">' +
'<div class="head"><i class="left dt"></i><i class="day"></i><i class="right dt"></i></div>' +
'<div class="week"><i>日</i><i>一</i><i>二</i><i>三</i><i>四</i><i>五</i><i>六</i></div>' +
'<div class="calbody"></div>' +
// time start
'<div class="time">' +
'<div class="row clearfix">' +
'<i class="tit">取车时间</i>' +
'<div class="slide"><div class="value" data-value="10:00"><em>10:00</em></div></div>' +
'</div>' +
'<div class="row clearfix">' +
'<i class="tit">还车时间</i>' +
'<div class="slide"><div class="value" data-value="10:00"><em>10:00</em></div></div>' +
'</div>' +
'</div>' +
// time end
'<div class="footbar"><a class="btn-red">确认</a></div>' +
'</div>';
$('body').append(ss);
this._createMonth();
}
Calendar.prototype._createMonth = function () {
var options = this.options;
var s0 = '<div class="month"><h2 data-m="{2}-{3}">{0}月</h2><div class="days">{1}</div></div>';
var s1 = '<i data-d="{0}"><em>{0}</em></i>';
var s2 = '<i class="disabled"><em>{0}</em></i>';
var ss = '';
var now = new Date();
var arr = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
for (var i = 0; i < options.months; i++) {
var dt = new Date(now.getFullYear(), now.getMonth() + i, 1);
let max = arr[dt.getMonth()];
if (dt.getFullYear() % 4 == 0 && dt.getMonth() == 1) max += 1;
var s = '',
d0 = 0;
// 从正确的周N开始-
while (d0 < dt.getDay()) {
s += s2.format('');
d0++;
}
for (var d = 1; d <= max; d++) {
var disabled = (i == 0) && (d < now.getDate() + options.minday);
s += (disabled ? s2 : s1).format(d);
}
// 不足一个月补全-
var d1 = 7 - (d0 + max) % 7;
while (d1 > 0) {
s += s2.format('');
d1--;
}
ss += s0.format(dt.getMonth() + 1, s, dt.getFullYear(), dt.getMonth());
}
$('.calbody').html(ss);
}
Calendar.prototype._resetDate = function () {
var arr = $(this.options.daysel + '.hover');
var d1 = arr.eq(0).addClass('start');
var d2 = arr.eq(1).addClass('end');
var next = d1.next();
while (!next.hasClass('end')) {
next.addClass('active');
next = next.next();
if (next.length == 0) {
next = d1.parent().parent().next().find('.days>i').eq(0);
}
}
this.setValue();
}
// 将最终的日期set到头部-
Calendar.prototype.setValue = function(){
var options = this.options;
var d1 = $(options.daysel + '.start');
var d2 = $(options.daysel + '.end');
var arr = $('.acal .time em');
var t1 = arr.eq(0).html() || '10:00';
var t2 = arr.eq(1).html() || '10:00';
function getDT(d, t){
var ym = d.parent().prev().data('m').split('-');
var s = '{0}-{1}-{2} {3}'.format(ym[0], ym[1]-0+1, d.data('d'), t);
return s.date(1);
}
options.start = getDT(d1, t1);
options.end = getDT(d2, t2);
$('.acal .head .dt').eq(0).html(options.start.date(4).replace('-','月').replace(' ','日 '));
$('.acal .head .dt').eq(1).html(options.end.date(4).replace('-','月').replace(' ','日 '));
$('.acal .head .day').html(av.days(options.start, options.end)+'天');
};
时间模块:
/**
* 滑动模块
* @param {object} options
* @param {jquery} options.bar 进度条
* @param {jquery} options.btn 按钮
* @param {Array} options.data 进度条上的数据
* @param {number} options.width 进度条宽度(可选)
* @param {function} options.callback 选择后的回调
*/
function Slider(options) {
this.options = options;
this.init();
}
Slider.prototype.init = function () {
var ins = this;
if (ins.options.steps) {
return; // 只init一次-
}
// 将整个bar分隔成N个宽度-
var steps = [];
var ww = ins.options.width || ins.options.bar[0].offsetWidth;
var w = (ww - 20) / (ins.options.data.length - 1);
$.each(ins.options.data, function (i, v) {
steps[i] = w * i;
});
ins.options.steps = steps;
var $v = ins.options.btn.parent();
var val = $v.data('value');
if (!val) {
val = ins.options.data[0];
}
ins.setValue(val);
$.each(ins.options.data, function (i, v) {
if (val == v) {
$v.width(ins.options.steps[i]);
}
});
// 绑定btn事件-
ins.bindEvent();
};
Slider.prototype.setValue = function (value) {
this.options.btn.html(value).parent().data('value', value);
};
Slider.prototype.bindEvent = function () {
var ins = this;
var $b = ins.options.btn;
var $v = $b.parent();
var x0 = 0, // 鼠标按下位置-
x1 = 0, // 鼠标移动过程的位置-
w0 = 0; // 滑动前的value大小-
$b.on('mousedown touchstart', function (event) {
x0 = event.clientX || event.touches[0].clientX;
w0 = $v.width();
w2 = ins.options.width || ins.options.bar[0].offsetWidth;
}).on('mousemove touchmove', function (event) {
if (x0 == 0) return;
x1 = event.clientX || event.touches[0].clientX;
var w1 = w0 + x1 - x0;
if (w1 > w2) return;
$v.width(w1);
var val = getNewValue(w1);
if (val != $v.data('value')) {
ins.setValue(val);
}
}).on('mouseup touchend', function (event) {
x0 = x1 = 0;
if(ins.options.callback){
ins.options.callback();
}
});
// 根据当前value宽度获取对应的data-
function getNewValue(w) {
var max = 0;
$.each(ins.options.steps, function (i, v) {
if (w > v) max = i;
});
return ins.options.data[max];
}
};
大家已经发现,该日历组件主要用于移动端项目,但JavaScript代码其实不区分客户端的,稍微修改CSS代码,一样用于PC端设备。
作者:朱会震