開発環境
MacBook Air (M1, 2020)
npm 8.18.0
"vue": "^3.2.13"
"dayjs": "^1.11.5"
本記事の内容
dayjsというライブラリを使ったカレンダーを作成します。
以下が目次です。
- 使用するものの作成
- カレンダーの作成
- より詳しい紹介
では解説していきます。
使用するものの作成
①使用するイベントデータ
②使用する型定義
使用するイベントデータ
events:[
{ id: 1, name: "宿題", date: "2022-10-01", color:"blue"},
{ id: 2, name: "遊び", date: "2022-10-10", color:"limegreen"},
{ id: 3, name: "ミーティング", date: "2022-10-15", color:"deepskyblue"},
{ id: 4, name: "", date: "2022-10-20", color:"dimgray"},
{ id: 5, name: "デート", date: "2022-10-31", color:"navy"},
]
使用する型定義
型を用意します。
export type EventItem={
id: number,
pet_id: number,
health: string,
detail: string,
start: Date,
end: Date,
color: "blue"| "red"|"limegreen"|"deepskyblue"|"dimgray"|"navy"|"orange"|"teal"|"royalblue",
isEditable: boolean
}
export type DateItem = {
date: number
month: string
dayEvents: EventItem[]
}
カレンダーの作成
手順は以下の11個です。
①カレンダーの最初の日を取得する。
②カレンダーの最後の日を取得する。
③一ヶ月の週の数分の配列の作成する。
④③で作成した配列を取得する。
⑤フォーマットを整える
⑥前の月を表示する。
⑦後ろの月を表示する。
⑧数字から曜日に変更を行うメソッドを定義する。
⑨背景色を分けるためのプロパティを作成する。
➉イベントを取得する。
⑪日付が一致するイベントを取得し、配列として格納するメソッドする。
では以下のコードを見ていきましょう。
<script setup lang="ts">
import dayjs from 'dayjs'
import { ref, computed } from 'vue'
import { EventItem } from '@/types/event';
import { useStore } from '../store'
const currentDate = ref(dayjs())
//①カレンダーの最初の日を取得する。
const getStartDate = () => {
//月の初めの日を取得する
let date = dayjs(currentDate.value).startOf("month");
const youbiNum = date.day();
//月の初めの日から日曜日までの差を算出し、日曜日の日を取得。
return date.subtract(youbiNum, "days");
}
//②カレンダーの最後の日を取得する。
const getEndDate = () => {
//月の最後の日を取得
let date = dayjs(currentDate.value).endOf("month");
const youbiNum = date.day();
//月の初めの日から土曜日までの差を算出し、土曜日の日を取得。
return date.add(6 - youbiNum, "days");
};
//③一ヶ月の週の数分の配列の作成
const getCalendar = () => {
let startDate: dayjs.Dayjs;
//月の初めの日
startDate = getStartDate();
//月最後の日
const endDate = getEndDate();
//カレンダーの縦軸を計算
const weekNumber = Math.ceil(endDate.diff(startDate, "days") / 7);
//空配列を用意
let calendars: DateItem[][] = [];
let calendarDate: dayjs.Dayjs;
calendarDate = getStartDate();
//7日分の日にちデータをループで取得してweekRowにpush。そしてweekRowの配列をcalendarsにpush。それをカレンダーの縦軸回数繰り返す処理
for (let week = 0; week < weekNumber; week++){
const weekRow = [];
for (let day = 0; day < 7; day++) {
let dayEvents: EventItem[] = getDayEvents(calendarDate)
weekRow.push({
date: calendarDate.get("date"),
month: calendarDate.format("YYYY-MM")//背景色を分けるため追加
dayEvents
})
calendarDate = calendarDate.add(1, "days");
}
calendars.push(weekRow);
}
return calendars;
}
//④③で作成した配列を取得
const calendars = computed(()=>{
return getCalendar()
})
//⑤フォーマットを整える
const displayMonth = computed(()=>{
return currentDate.value.format('YYYY[年]M[月]')
})
//⑥前の月を表示
const prevMonth = () => {
currentDate.value= dayjs(currentDate.value).subtract(1, "month")
}
//⑦後ろの月を表示
const nextMonth = () => {
currentDate.value = dayjs(currentDate.value).add(1, "month")
}
//⑧数字から曜日に変更を行うメソッドを定義
const youbi = (dayIndex: number) => {
const week = ["日", "月", "火", "水", "木", "金", "土"];
return week[dayIndex];
}
//⑨背景色を分けるためのプロパティ
const currentMonth= computed(()=>{
return currentDate.value.format('YYYY-MM')
})
const store = useStore()
//⑩イベントを取得
const events = computed<EventItem[]>(()=>{
return store.getters['calendar/getEvents']
})
//⑪日付が一致するイベントを取得し、配列として格納するメソッド
const getDayEvents = (date: dayjs.Dayjs) => {
return events.value.filter((event: EventItem)=> {
let eventDate = dayjs(event.start).format('YYYY-MM-DD')
let Date = date.format('YYYY-MM-DD')
if(eventDate == Date) return true;
});
}
</script>
<template>
<div>
<div class="content">
<p>{{currentDate}}</p>
<h3></h3>
<h2>現在の日付:{{ displayMonth }}</h2>
<div class="button-area">
<button @click="prevMonth" class="button">前の月</button>
<button @click="nextMonth" class="button">次の月</button>
</div>
<div class="calendar">
<div class="calendar-weekly">
<div class="calendar-youbi" v-for="n in 7" :key="n">
{{ youbi(n-1) }}
</div>
</div>
<div class="calendar-weekly" v-for="(week, index) in calendars" :key="index">
<div class="calendar-daily" :class="{outside: currentMonth !== day.month}" v-for="(day, index) in week" :key="index">
<div class="calendar-day">
<p>{{ day.date }}</p>
</div>
<!--currnetMonthの値とday.monthの値が異なる場合のみstyleを適用-->
<div v-for="dayEvent in day.dayEvents" :key="dayEvent.id" >
<div class="calendar-event" :style="`background-color:${dayEvent.color}`" >
<p>{{ dayEvent.health }}</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style>
/* 全体調整*/
.content{
/* 中央よせ*/
margin:2em auto;
/* 大きさ調整*/
width:900px;
}
.button-area{
margin:0.5em 0;
}
/* ボタン調整*/
.button{
padding:4px 8px;
margin-right:8px;
}
.calendar{
/* 上横線*/
border-top:1px solid #E0E0E0;
/* カレンダーフォントサイズ*/
font-size:0.8em;
}
.calendar-weekly{
/* 週ごとに横並び*/
display:flex;
/* 左縦線*/
border-left:1px solid #E0E0E0;
}
/* 日にちのスタイル*/
.calendar-daily{
/* 横幅*/
flex:1;
/* 大きさ調節*/
min-height:125px;
/* 右縦線*/
border-right:1px solid #E0E0E0;
/* 下横線*/
border-bottom:1px solid #E0E0E0;
}
/* 曜日のスタイル*/
.calendar-youbi{
/* 横幅調節*/
flex:1;
/* 縦線*/
border-right:1px solid #E0E0E0;
}
/* 背景色*/
.outside{
background-color: #f7f7f7;
}
/* イベント調整*/
.calendar-event{
/* フォント色付け*/
color:white;
line-height:25px;
/* 丸みを帯びさせる*/
border-radius:4px;
}
</style>
より詳しい解説
ちょっとわかりづらい、8、9、10の3つのの3つについてより詳しい解説していきます。
数字から曜日に変更を行うメソッドを定義
タイトルの通り、数字を曜日に変更するメソッドです。
v-forを使い、7を順番に展開。
youbiメソッドを実行して数字を曜日に変更しています。
forで展開する場合、1から始まるので、-1としています。
<script setup lang="ts">
const youbi = (dayIndex: number) => {
const week = ["日", "月", "火", "水", "木", "金", "土"];
return week[dayIndex];
}
</script>
<template>
<div class="calendar">
<div class="calendar-weekly">
<div class="calendar-youbi" v-for="n in 7" :key="n">
{{ youbi(n-1) }}
</div>
</template>
背景色を分けるためのプロパティ
ここでの目的は、currentMonthとの比較です。
そのためにフォーマットを整えます。
<script setup lang="ts">
const currentMonth= computed(()=>{
return currentDate.value.format('YYYY-MM')
})
const getCalendar = () => {
//省略
let calendars: DateItem[][] = [];
let calendarDate: dayjs.Dayjs;
calendarDate = getStartDate();
for (let week = 0; week < weekNumber; week++){
const weekRow: DateItem[] = [];
for (let day = 0; day < 7; day++) {
let dayEvents: EventItem[] = getDayEvents(calendarDate)
weekRow.push({
date: calendarDate.get("date"),
month: calendarDate.format("YYYY-MM")//背景色を分けるため追加
dayEvents
})
calendarDate = calendarDate.add(1, "days");
}
calendars.push(weekRow);
}
return calendars;
}
</script>
<template>
<!--currentMonthとday.monthが異なる場合のみcssを適用-->
<div class="calendar-daily" :class="{outside: currentMonth !== day.month}" v-for="(day, index) in week" :key="index">
<div class="calendar-day">
<p>{{ day.date }}</p>
</div>
</template>
日付が一致するイベントを取得し、配列として格納するメソッド
イベントという配列に格納されている情報を、カレンダーの日付と一致する場合に渡す処理を行なっています。
<script setup lang="ts">
const getCalendar = () => {
//省略
let calendars: DateItem[][] = [];
let calendarDate: dayjs.Dayjs;
calendarDate = getStartDate();
for (let week = 0; week < weekNumber; week++){
const weekRow: DateItem[] = [];
for (let day = 0; day < 7; day++) {
let dayEvents: EventItem[] = getDayEvents(calendarDate)
weekRow.push({
date: calendarDate.get("date"),
month: calendarDate.format("YYYY-MM")//背景色を分けるため追加
dayEvents
})
calendarDate = calendarDate.add(1, "days");
}
calendars.push(weekRow);
}
return calendars;
}
//渡されてきたcurrentDateとイベントに格納されている日付データと一致する場合にその情報を配列にして渡す。
const getDayEvents = (date: dayjs.Dayjs) => {
return events.value.filter((event: EventItem)=> {
let eventDate = dayjs(event.start).format('YYYY-MM-DD')
let Date = date.format('YYYY-MM-DD')
if(eventDate == Date) return true;
});
}
</script>