手写滚动日期选择器vue
时间: 2023-07-01 14:05:45 浏览: 202
以下是一个简单的手写滚动日期选择器的 Vue 组件示例代码:
```html
<template>
<div class="scroll-date-picker">
<div class="scroll-container">
<div class="scroll-item" v-for="(item, index) in yearList" :key="index" :style="{ top: (index * itemHeight) + 'px' }">{{ item }}</div>
<div class="scroll-overlay" ref="yearOverlay"></div>
</div>
<div class="scroll-container">
<div class="scroll-item" v-for="(item, index) in monthList" :key="index" :style="{ top: (index * itemHeight) + 'px' }">{{ item }}</div>
<div class="scroll-overlay" ref="monthOverlay"></div>
</div>
<div class="scroll-container">
<div class="scroll-item" v-for="(item, index) in dayList" :key="index" :style="{ top: (index * itemHeight) + 'px' }">{{ item }}</div>
<div class="scroll-overlay" ref="dayOverlay"></div>
</div>
</div>
</template>
<script>
export default {
name: 'ScrollDatePicker',
props: {
value: {
type: String,
default: ''
},
startDate: {
type: String,
default: ''
},
endDate: {
type: String,
default: ''
},
dateFormat: {
type: String,
default: 'YYYY-MM-DD'
},
itemHeight: {
type: Number,
default: 40
}
},
data() {
return {
yearList: [],
monthList: [],
dayList: [],
curYear: '',
curMonth: '',
curDay: ''
}
},
computed: {
minValue() {
return this.startDate ? new Date(this.startDate.replace(/-/g, '/')) : new Date('1970/01/01')
},
maxValue() {
return this.endDate ? new Date(this.endDate.replace(/-/g, '/')) : new Date('2099/12/31')
},
curDate() {
return this.value ? new Date(this.value.replace(/-/g, '/')) : new Date()
},
daysInMonth() {
return new Date(this.curYear, this.curMonth + 1, 0).getDate()
}
},
watch: {
value(val) {
if (!val) return
const date = new Date(val.replace(/-/g, '/'))
this.curYear = date.getFullYear() - this.minValue.getFullYear()
this.curMonth = date.getMonth()
this.curDay = date.getDate() - 1
}
},
mounted() {
this.initData()
this.bindEvents()
},
methods: {
initData() {
const yearCount = this.maxValue.getFullYear() - this.minValue.getFullYear() + 1
for (let i = 0; i < yearCount; i++) {
this.yearList.push(this.minValue.getFullYear() + i)
}
for (let i = 1; i <= 12; i++) {
this.monthList.push(i)
}
for (let i = 1; i <= this.daysInMonth; i++) {
this.dayList.push(i)
}
this.curYear = this.curDate.getFullYear() - this.minValue.getFullYear()
this.curMonth = this.curDate.getMonth()
this.curDay = this.curDate.getDate() - 1
},
bindEvents() {
this.$refs.yearOverlay.addEventListener('touchstart', this.handleTouchStart.bind(this))
this.$refs.yearOverlay.addEventListener('touchmove', this.handleTouchMove.bind(this))
this.$refs.yearOverlay.addEventListener('touchend', this.handleTouchEnd.bind(this))
this.$refs.monthOverlay.addEventListener('touchstart', this.handleTouchStart.bind(this))
this.$refs.monthOverlay.addEventListener('touchmove', this.handleTouchMove.bind(this))
this.$refs.monthOverlay.addEventListener('touchend', this.handleTouchEnd.bind(this))
this.$refs.dayOverlay.addEventListener('touchstart', this.handleTouchStart.bind(this))
this.$refs.dayOverlay.addEventListener('touchmove', this.handleTouchMove.bind(this))
this.$refs.dayOverlay.addEventListener('touchend', this.handleTouchEnd.bind(this))
},
handleTouchStart(event) {
event.preventDefault()
const touches = event.touches[0]
this.startY = touches.pageY
this.startTop = event.target.parentNode.scrollTop
},
handleTouchMove(event) {
event.preventDefault()
const touches = event.touches[0]
const deltaY = touches.pageY - this.startY
event.target.parentNode.scrollTop = this.startTop - deltaY
},
handleTouchEnd(event) {
event.preventDefault()
const scrollTop = event.target.parentNode.scrollTop
const index = Math.round(scrollTop / this.itemHeight)
if (event.target === this.$refs.yearOverlay) {
this.curYear = index
this.updateDayList()
} else if (event.target === this.$refs.monthOverlay) {
this.curMonth = index
this.updateDayList()
} else if (event.target === this.$refs.dayOverlay) {
this.curDay = index
}
this.emitDate()
this.scrollToIndex()
},
updateDayList() {
this.dayList = []
for (let i = 1; i <= this.daysInMonth; i++) {
this.dayList.push(i)
}
if (this.curDay >= this.daysInMonth) {
this.curDay = this.daysInMonth - 1
}
},
emitDate() {
const date = new Date(this.minValue.getFullYear() + this.curYear, this.curMonth, this.curDay + 1)
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
this.$emit('input', `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`)
},
scrollToIndex() {
this.$refs.yearOverlay.parentNode.scrollTop = this.curYear * this.itemHeight
this.$refs.monthOverlay.parentNode.scrollTop = this.curMonth * this.itemHeight
this.$refs.dayOverlay.parentNode.scrollTop = this.curDay * this.itemHeight
}
}
}
</script>
<style scoped>
.scroll-date-picker {
display: flex;
align-items: center;
justify-content: space-between;
height: 120px;
overflow: hidden;
}
.scroll-container {
flex: 1;
height: 100%;
overflow-y: scroll;
-webkit-overflow-scrolling: touch;
position: relative;
}
.scroll-item {
position: absolute;
left: 0;
right: 0;
font-size: 16px;
line-height: 40px;
text-align: center;
}
.scroll-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
height: 40px;
}
</style>
```
在模板中,我们使用了三个滚动容器,分别用于显示年、月、日的可选项。每个滚动容器中包含了一组滚动项和一个滚动遮罩层,遮罩层用于接收 touch 事件并计算滚动距离。
在 JavaScript 部分,我们定义了一些组件属性和响应式数据,包括:
- value: 当前日期值,格式为 YYYY-MM-DD。
- startDate: 可选起始日期,格式为 YYYY-MM-DD。
- endDate: 可选结束日期,格式为 YYYY-MM-DD。
- dateFormat: 日期格式,用于将日期字符串转换为 Date 对象。
- itemHeight: 每个滚动项的高度,用于计算滚动距离。
- yearList: 年份选项列表。
- monthList: 月份选项列表。
- dayList: 日份选项列表。
- curYear: 当前选中的年份索引。
- curMonth: 当前选中的月份索引。
- curDay: 当前选中的日份索引。
我们使用了 computed 属性来计算一些派生数据:
- minValue: 最小可选日期。
- maxValue: 最大可选日期。
- curDate: 当前日期对象。
- daysInMonth: 当前月份总天数。
在 mounted 钩子函数中,我们初始化了年、月、日选项列表,并设置了当前选中的年、月、日索引。
我们还定义了一些辅助方法,包括:
- initData: 初始化选项列表数据。
- bindEvents: 绑定 touch 事件。
- handleTouchStart: 处理 touchstart 事件,保存当前滚动距离。
- handleTouchMove: 处理 touchmove 事件,计算滚动距离并更新 scrollTop。
- handleTouchEnd: 处理 touchend 事件,计算选中的索引并更新 curYear、curMonth、curDay,然后触发 emitDate 事件和 scrollToIndex 方法。
- updateDayList: 更新日份选项列表,根据当前选中的年、月计算出对应的总天数。
- emitDate: 触发 input 事件,将当前日期字符串传递给父组件。
- scrollToIndex: 滚动到当前选中的年、月、日索引位置。
最后,我们将组件中的滚动容器和滚动项进行了一些样式定义,使其能够正常显示和滚动。
阅读全文