html
<div class="date-picker">
<div class="scroll-container">
<div class="target-center"></div>
<div class="mask"></div>
<div class="wrapper year-wrapper">
<div class="scroll-wrapper">
<!-- 预留顶部和底部4个(等于200px)
<div class="item"></div>
<div class="item"></div>
<div class="item">2000</div>
<div class="item">2001</div>
<div class="item">2002</div>
<div class="item"></div>
<div class="item"></div> -->
</div>
</div>
</div>
<div class="scroll-container">
<div class="target-center"></div>
<div class="mask"></div>
<div class="wrapper month-wrapper">
<div class="scroll-wrapper">
</div>
</div>
</div>
<div class="scroll-container">
<div class="target-center"></div>
<div class="mask"></div>
<div class="wrapper day-wrapper">
<div class="scroll-wrapper">
</div>
</div>
</div>
</div>
ts
type typeCallback = (currentDate: number[]) => void;
interface IDatePicker {
getCurrentDate(): number[];
watch(callback: typeCallback): void;
}
enum DateMark {
YEAR = "YEAR",
MONTH = "MONTH",
DAY = "DAY",
}
class DatePicker {
private yearArr: number[];
private monthArr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
private dayArr: number[];
private currentDate: number[] = new Array<number>(3);
private callback: typeCallback;
private yearWrapper: HTMLElement = document.querySelector(".year-wrapper")!;
private monthWrapper: HTMLElement =
document.querySelector(".month-wrapper")!;
private dayWrapper: HTMLElement = document.querySelector(".day-wrapper")!;
constructor(yearArr: number[], currentDate: number[]) {
// 赋值年列
this.yearArr = yearArr;
this.currentDate = currentDate;
// 赋值日列 根据当前年和月
this.dayArr = this.createDayArr(this.currentDate);
// 初始化3列
this.initDom();
// 绑定事件
this.bindEvent();
}
// 创建 天 数组
private createDayArr([year, month]: number[]): number[] {
const count = getDayCount(year, month);
const arr: number[] = [];
for (let i = 1; i <= count; i++) {
arr.push(i);
}
return arr;
}
private initDom() {
this.createItems(this.yearWrapper, this.yearArr);
this.createItems(this.monthWrapper, this.monthArr);
this.createItems(this.dayWrapper, this.dayArr);
this.initCurrentDate(this.currentDate);
}
// 初始化当前时间的年月日初始高度
private initCurrentDate([year, month, day]: typeof this.currentDate) {
const yearIndex = this.yearArr.indexOf(year);
const monthIndex = this.monthArr.indexOf(month);
const dayIndex = this.dayArr.indexOf(day);
setScrollY(this.yearWrapper, yearIndex);
setScrollY(this.monthWrapper, monthIndex);
setScrollY(this.dayWrapper, dayIndex);
}
private bindEvent() {
this.yearWrapper.addEventListener(
"scrollend",
this.setCurrentDate.bind(this, this.yearWrapper, "YEAR"),
false
);
this.monthWrapper.addEventListener(
"scrollend",
this.setCurrentDate.bind(this, this.monthWrapper, "MONTH"),
false
);
this.dayWrapper.addEventListener(
"scrollend",
this.setCurrentDate.bind(this, this.dayWrapper, "DAY"),
false
);
}
private setCurrentDate(wrapper: HTMLElement, field: DateMark) {
// 根据滚动高度除以每条item50像素高度
const index = wrapper.scrollTop / 50;
if (field === DateMark.YEAR) {
this.currentDate[0] = this.yearArr[index];
this.dayArr = this.createDayArr(this.currentDate);
this.createItems(this.dayWrapper, this.dayArr);
this.callback && this.callback(this.currentDate);
}
if (field === DateMark.MONTH) {
this.currentDate[1] = this.monthArr[index];
this.dayArr = this.createDayArr(this.currentDate);
this.createItems(this.dayWrapper, this.dayArr);
this.callback && this.callback(this.currentDate);
}
if (field === DateMark.DAY) {
this.currentDate[2] = this.dayArr[index];
this.callback && this.callback(this.currentDate);
}
}
// 初始化列数据 - 动态创建列数据
private createItems(wrapper: HTMLElement, arr: number[]) {
const frag = document.createDocumentFragment();
// 先创建2空的个item;
for (let i = 0; i < 2; i++) {
const item = createItem();
frag.appendChild(item);
}
arr.forEach((year) => {
const item = createItem();
item.innerText = year + "";
frag.appendChild(item);
});
// 后创建2空的个item;
for (let i = 0; i < 2; i++) {
const item = createItem();
frag.appendChild(item);
}
// 清空上一次创建的数据
const scrollWrapper = wrapper.querySelector(".scroll-wrapper")!;
scrollWrapper.innerHTML = "";
scrollWrapper.appendChild(frag);
}
// 获取当前日期
public getCurrentDate(): number[] {
return this.currentDate;
}
public watch(callback: typeCallback): void {
typeof callback === "function" && (this.callback = callback);
}
}
// 获取多少天根据年和月;
function getDayCount(year: number, month: number): number {
return new Date(year, month, 0).getDate();
}
// 创建div元素
function createItem(): HTMLElement {
const item = document.createElement("div");
item.classList.add("item");
return item;
}
function setScrollY(wrapper: HTMLElement, index: number) {
wrapper.scrollTo(0, index * 50);
}
const datePicker = new DatePicker([2019, 2020, 2021], [2020, 2, 27]);
const getDate = datePicker.getCurrentDate();
console.log('获取当前选择的日期:', getDate);
datePicker.watch((getDate)=>{
console.log('监听当前选择的日期:', getDate);
})
css
.date-picker{
display: flex;
flex-direction: row;
width: 100%;
height: 250px;
border: 1px solid black;
}
.scroll-container{
position: relative;
width: 100%;
height: 100%;
}
.scroll-container .wrapper{
overflow-y: auto;
height: 250px;
scroll-snap-type: y mandatory;
}
.wrapper .scroll-wrapper .item{
display: flex;
justify-content: center;
align-items: center;
height: 50px;
/* 滚动后吸附效果停在中间 */
scroll-snap-align: center;
}
/* 去除滚动条 */
::-webkit-scrollbar{
display: none;
}
.scroll-container .target-center{
position: absolute;
top: 100px;
left: 0;
z-index: -1;
width: 100%;
height: 50px;
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
box-sizing: border-box;
}
.scroll-container .mask{
position: absolute;
top: 0;
left: 0;
z-index: 1;
width: 100%;
height: 100%;
pointer-events: none;
/* 设置可见清晰度 */
background-image: linear-gradient(
to top,
hsla(0, 0%, 100%, 1),
hsla(0, 0%, 100%, .8),
hsla(0, 0%, 100%, 0),
hsla(0, 0%, 100%, .8),
hsla(0, 0%, 100%, 1)
);
}