vue实现日历周滑动
Vue 实现日历周滑动功能
核心思路
日历周滑动功能通常需要结合日期计算、动态渲染和触摸事件处理。Vue 的响应式特性可以轻松实现数据与视图的同步更新。
日期计算逻辑
使用 Date 对象或第三方库(如 date-fns)处理日期增减。以下为计算当前周日期范围的示例代码:
function getWeekDates(baseDate) {
const start = new Date(baseDate);
start.setDate(start.getDate() - start.getDay()); // 调整到周日
const dates = [];
for (let i = 0; i < 7; i++) {
const date = new Date(start);
date.setDate(date.getDate() + i);
dates.push(date);
}
return dates;
}
组件数据设计
在 Vue 组件中维护当前基准日期:

data() {
return {
currentDate: new Date(), // 基准日期
weekDates: [] // 存储当前周日期
};
},
created() {
this.updateWeekDates();
},
methods: {
updateWeekDates() {
this.weekDates = getWeekDates(this.currentDate);
}
}
滑动事件处理
通过 @touchstart 和 @touchend 实现滑动检测:
data() {
return {
touchStartX: 0
};
},
methods: {
handleTouchStart(e) {
this.touchStartX = e.touches[0].clientX;
},
handleTouchEnd(e) {
const diffX = e.changedTouches[0].clientX - this.touchStartX;
if (Math.abs(diffX) > 50) { // 滑动阈值
diffX > 0 ? this.prevWeek() : this.nextWeek();
}
},
prevWeek() {
const date = new Date(this.currentDate);
date.setDate(date.getDate() - 7);
this.currentDate = date;
this.updateWeekDates();
},
nextWeek() {
const date = new Date(this.currentDate);
date.setDate(date.getDate() + 7);
this.currentDate = date;
this.updateWeekDates();
}
}
模板渲染
在模板中绑定事件和渲染日期:

<div
class="calendar-week"
@touchstart="handleTouchStart"
@touchend="handleTouchEnd"
>
<div v-for="(date, index) in weekDates" :key="index" class="day">
<div class="day-name">{{ formatDayName(date) }}</div>
<div class="date">{{ date.getDate() }}</div>
</div>
</div>
样式优化
添加 CSS 过渡效果提升用户体验:
.calendar-week {
display: flex;
overflow-x: auto;
transition: transform 0.3s ease;
}
.day {
flex: 1;
text-align: center;
padding: 10px;
}
完整组件示例
整合以上代码的完整组件实现:
<template>
<div
class="calendar-container"
@touchstart="handleTouchStart"
@touchend="handleTouchEnd"
>
<div class="header">
<button @click="prevWeek">←</button>
<span>{{ monthYear }}</span>
<button @click="nextWeek">→</button>
</div>
<div class="calendar-week">
<div
v-for="(date, index) in weekDates"
:key="index"
class="day"
:class="{ 'today': isToday(date) }"
>
<div class="day-name">{{ formatDayName(date) }}</div>
<div class="date">{{ date.getDate() }}</div>
</div>
</div>
</div>
</template>
<script>
import { format } from 'date-fns';
export default {
data() {
return {
currentDate: new Date(),
touchStartX: 0
};
},
computed: {
weekDates() {
return this.getWeekDates(this.currentDate);
},
monthYear() {
return format(this.currentDate, 'MMMM yyyy');
}
},
methods: {
getWeekDates(baseDate) {
const start = new Date(baseDate);
start.setDate(start.getDate() - start.getDay());
const dates = [];
for (let i = 0; i < 7; i++) {
const date = new Date(start);
date.setDate(date.getDate() + i);
dates.push(date);
}
return dates;
},
isToday(date) {
return format(date, 'yyyy-MM-dd') === format(new Date(), 'yyyy-MM-dd');
},
formatDayName(date) {
return format(date, 'EEE');
},
handleTouchStart(e) {
this.touchStartX = e.touches[0].clientX;
},
handleTouchEnd(e) {
const diffX = e.changedTouches[0].clientX - this.touchStartX;
if (Math.abs(diffX) > 50) {
diffX > 0 ? this.prevWeek() : this.nextWeek();
}
},
prevWeek() {
const date = new Date(this.currentDate);
date.setDate(date.getDate() - 7);
this.currentDate = date;
},
nextWeek() {
const date = new Date(this.currentDate);
date.setDate(date.getDate() + 7);
this.currentDate = date;
}
}
};
</script>
<style scoped>
.calendar-container {
user-select: none;
}
.header {
display: flex;
justify-content: space-between;
padding: 10px;
}
.calendar-week {
display: flex;
}
.day {
flex: 1;
padding: 10px;
}
.today {
background-color: #e3f2fd;
}
</style>
注意事项
- 移动端适配需考虑
touch事件,PC 端可添加鼠标事件模拟 - 性能优化:对于复杂日历可考虑虚拟滚动
- 国际化:使用
date-fns的本地化功能支持多语言 - 周起始日:根据地区需求调整(中国通常周一为第一天)






