实现一个简易版的时间选择器
实现效果:
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/f105cb7c89f609a8826aefcfb5700cb0.gif)
逻辑分析与代码实现
数据
- date(year,month,day) 构造函数,month从 0 - 11分别对应一月到十二月,day设置为0时,是上一个月的最后一天,如果设置为当前月最大天的下一天,则为下一个月的第一天。
- 对于选择日期,一个月最少28天,再考虑到月初和月末可能和邻近日期不在同一周的情况,因此我们需要一个6*7的二维数组去储存,日期的更改实际上是对于这个数组的更新。
- 每个数组元素储存一个对象,此对象包含 label:当前日期,date:对应年月日,isMonth:是否为当前月的元素,渲染时直接每个单元格对应一个数组元素,显示label,根据isMonth判断分属不同的类,判断默认日期激活active
function datePicker() {
let date = new Date();
// 选择的这一天
this.currentDay = {
year: date.getFullYear(),
month: date.getMonth(),
day: date.getDate(),
};
// 表头
this.weeks = [ '日', '一', '二', '三', '四', '五', '六' ];
this.dateArray = [];
this.changeDateArray();
}
datePicker.prototype.changeDate = function (type , value){
this.dateArray.splice(0, this.dateArray.length);
if(type == 'year'){
this.currentDay.year = value ;
}
else if(type == 'month'){
this.currentDay.month = value %12;
this.currentDay.year += parseInt(value/12);
console.log(value)
}
else if(type == 'day'){
this.currentDay.day = value;
}
this.changeDateArray();
}
datePicker.prototype.changeDateArray = function f() {
let firstDay = new Date(this.currentDay.year, this.currentDay.month, 1);
let offsetDay = -firstDay.getDay() + 1;
for (let i = 0; i < 6; i++) {
let arrayRow = [];
for (let j = 0; j < 7; j++) {
const day = i * 7 + j + offsetDay;
const dayObject = new Date(this.currentDay.year, this.currentDay.month, day);
let mState = false;
let dState = false;
if (dayObject.getMonth() == this.currentDay.month) {
mState = true;
}
if (dayObject.getDate() == this.currentDay.day) {
dState = true;
}
arrayRow.push({
theDate: getFormat(dayObject),
label: dayObject.getDate(),
monthState: mState,
dayState: dState,
});
}
this.dateArray.push(arrayRow);
}
};
function getFormat( date ) {
let year = date.getFullYear();
let month = date.getMonth();
let day = date.getDate();
return year + '-' + (month + 1) + '-' + day;
}
选择面板部分
- 点击input当focus后,显示选择面板,默认显示当前月份,然后在顶栏可以进行年份和月份的选择
- 选择年份,则弹出今年所在的10年段,选择月份,则显示十二月份
- 同时需要储存一个对象保存选择的年月日,根据这个进行date的构造
控制面板显示
- 当显示默认面板之后,再次点击input,则仍处于focus状态,不做处理
- 当点击非input位置,即input.onblur时,则进入判断,如果是在面板区域则恢复focus,如果不是在面板区域则设置visible为false
Input.onblur = function(){
if (如果鼠标点击位置在面板内) {
Input.focus();
return;
}
else{
document.onmousedown = null;
_this.datePanelVisible = false;
};
}
- 判断是否在面板区域,则需要读取鼠标位置,但由于鼠标位置是相对于body的,所以我们也需要获得面板相对于body的位置,el.offsetLeft是相对于定位元素的left偏移量,el.offsetParent是定位元素,因此可以通过向上寻找定位元素直到body并累加偏移量的方式,获得当前元素与body的偏移量。
let left = 0;
let top = 0;
while( el.tagname !== 'body'){
left += el.offsetLeft;
top += el.offsetTop;
el = el.offsetParent;
}
难点
- 面板内部的交互逻辑,div结构和数据
- 控制面板显示部分的交互,涉及到input的focus和blur,以及如何获取元素相对于body的位置
- 如何维护更新控制数据的二维数组