JavaScriptでCanvasを使用し、選択した写真と記念日を記したカレンダーを表示するアプリをChatGPTに指示をして作りました。
使用した言語など
今回はHTMLのペラページに以下のような言語やツールを使用して作成しました。
言語
-
HTML
-
CSS
-
JavaScript
ツール・フォント・ライブラリ
-
ChatGPT
-
Flatpickr(日付選択ライブラリ)
-
Copse(Googleフォント)
使い方
1.画像を選択
画面上部の「画像を選択」ボタンをクリックし、背景にしたい写真をアップロード。選んだ画像が自動的にCanvasの左側に表示されます。
2.日付を選択
「日付を選択」ボタンをクリックし、記念日として表示したい日付をカレンダーから選択。選択した日付がカレンダーに反映され、記念日として強調表示されます。
3.ダウンロード
画面下部の「ダウンロード」アイコンが表示されたボタンをクリックすると、Canvasに表示された内容が画像ファイルとして保存されます。
機能別ChatGPTへの指示内容
以下、各機能別に指示内容のサンプルをまとめました。ChatGPTを活用して生成されたコードをベースにしていますが、指示内容はさらに細かく、生成されたコードも全てそのまま使用したわけではありません。
生成されたコードについて再度ChatGPTに解説してもらい、必要に応じて手動で修正や調整を加えています。
画像選択機能
機能
-
画像選択ボタンを設置し、ユーザーが任意の写真を背景に設定できるようにする。
主な指示内容
-
画像選択ボタンを押すと、画像ファイルを選択可能にする。
-
選択された画像がCanvasに読み込まれるようにする。
日付選択機能
機能
-
日付選択ボタンを設置し、Flatpickrライブラリを用いて、記念日の日付をユーザーが選択できるようにする。
主な指示内容
-
Flatpickrを用いて、選択された日付をCanvas上にカレンダーとして反映する。
-
カレンダーは日付選択ボタンの下に表示する。
-
日付変更時にはカレンダーが自動更新され、選択した日付をボタンに表示にする。
Canvas設定(背景画像とカレンダー表示)
背景画像の表示
機能
-
Canvasの左側に背景画像を表示し、約55%の幅を占めるようにする。
主な指示内容
-
ユーザーが選択した画像をCanvasにトリミングして表示する。
-
アスペクト比を保ち、CSSのobject-fit: cover;のように画像の中心が美しく収まるようにする。
-
トリミングされた画像をCanvasの左側に約55%の幅で表示。
カレンダー表示と記念日
機能
-
Canvasの右側にカレンダーを表示し、Canvasの約45%を使用する。
主な指示内容
-
カレンダーにはGoogleフォントのCopseを使用し、卓上カレンダーのように上から順に年と月、曜日、日付を表示。
-
数字の月の下には英語の月も表示して、年と月は中央に配置され、視覚的に目立つよう大きなフォントサイズを設定。
-
曜日ごとに色分け(日曜は赤、土曜は青、平日は黒)し「Sun、Mon、Tue」のように出力。
-
選択された日付が角丸枠で強調され、赤色の枠で塗り潰し、日付が白色で強調表示されるように設定。
-
曜日や日付のサイズ・間隔を計算し、画面幅が変更された場合にもレイアウトが崩れないように調整。
-
カレンダーの左右に30pxの余白を設定
デザインとレイアウト
機能
-
全体の見た目と操作性を向上させるため、UIのデザインとレイアウトを整える。
主な指示内容
-
Canvas、画像選択ボタン、日付選択ラベルが画面中央に配置されるように設定。
-
ボタンにはホバー時の色変化や角の丸みを加えて、操作性を向上。
ダウンロード機能
機能
-
Canvasに表示されたカレンダーを画像ファイルとしてダウンロードできるようにする。
主な指示内容
-
ダウンロードボタンを押すと、Canvas内容を画像として保存されるように設定。
機能別ソースコード解説
1. 画像選択機能
// JavaScriptコード
document.getElementById('image-input').addEventListener('change', function (event) {
const file = event.target.files[0];
const reader = new FileReader();
reader.onload = function (e) {
const img = new Image();
img.crossOrigin = 'anonymous'; // クロスオリジン設定
img.onload = function () {
selectedImage = img;
drawCanvas(); // 画像が読み込まれたら描画
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
});
概要
-
ユーザーが画像選択ボタンで画像を選ぶと、その画像をCanvas上に表示する機能
解説
-
FileReaderを使用して、ユーザーが選択した画像ファイルを読み込む
-
画像が読み込まれた後、selectedImage変数に格納し、drawCanvas()関数を呼び出してCanvas上に描画
-
crossOriginプロパティを設定することで、画像のクロスオリジン制限を回避
2. 日付選択機能
// JavaScriptコード
flatpickr("#date-input", {
defaultDate: new Date(),
onChange: function(selectedDates, dateStr, instance) {
document.getElementById('date-text').textContent = dateStr || '日付を選択';
drawCanvas(); // 日付が変更されたときに再描画
},
appendTo: document.body,
positionElement: document.getElementById('date-label')
});
概要
-
Flatpickrライブラリを使用して、ユーザーが記念日の日付を選択できるようにし、Canvasに反映する機能
解説
-
flatpickrを使用して、日付選択用のカレンダーUIを提供
-
onChangeイベントで日付が変更されるたびにdrawCanvas()関数を呼び出し、Canvasを更新
-
選択した日付はdate-text要素に表示され、未選択の場合は「日付を選択」と表示
3. Canvas設定(背景画像とカレンダー表示)
背景画像の表示
// JavaScriptコード
function cropImageToFitCanvas(image, targetWidth, targetHeight) {
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
tempCanvas.width = targetWidth;
tempCanvas.height = targetHeight;
const imageAspectRatio = image.width / image.height;
const canvasAspectRatio = targetWidth / targetHeight;
let drawWidth, drawHeight, offsetX = 0, offsetY = 0;
// アスペクト比を維持しつつ、画像をトリミングして仮のCanvasに描画
if (imageAspectRatio > canvasAspectRatio) {
drawHeight = targetHeight;
drawWidth = drawHeight * imageAspectRatio;
offsetX = (drawWidth - targetWidth) / 2;
} else {
drawWidth = targetWidth;
drawHeight = drawWidth / imageAspectRatio;
offsetY = (drawHeight - targetHeight) / 2;
}
// 仮のCanvasに画像をトリミングして描画
tempCtx.drawImage(image, -offsetX, -offsetY, drawWidth, drawHeight);
return tempCanvas;
}
概要
-
画像のアスペクト比を維持したまま、Canvasの左側に適切に収まるようトリミングする機能
解説
-
一時的なtempCanvasを作成し、画像をトリミング
-
画像とCanvasのアスペクト比を比較し、画像のサイズと位置を計算
-
トリミングした画像をdrawImage()で描画し、結果を返す
カレンダー表示と記念日
function drawCanvas() {
const imgWidth = canvas.width * 0.55;
const imgHeight = canvas.height;
ctx.clearRect(0, 0, canvas.width, canvas.height); // カレンダー用Canvasをクリア
// 背景を白で塗りつぶす
ctx.fillStyle = '#fdfefd';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// トリミングした画像を描画
if (selectedImage) {
const croppedImage = cropImageToFitCanvas(selectedImage, imgWidth, imgHeight);
ctx.drawImage(croppedImage, 0, 0, imgWidth, imgHeight); // 左側にトリミングされた画像を描画
}
// カレンダー表示に必要な年月と選択された日付
const selectedDate = new Date(document.getElementById('date-input').value);
const year = selectedDate.getFullYear();
const month = selectedDate.getMonth();
const day = selectedDate.getDate();
const daysInMonth = new Date(year, month + 1, 0).getDate();
const firstDayOfMonth = new Date(year, month, 1).getDay(); // 月の初日の曜日
const monthNames = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
// カレンダーの配置位置(右エリアはCanvasの45%)
const calendarWidth = canvas.width * 0.45 - 60; // 左右に30pxの余白を設定
const calendarX = canvas.width * 0.55 + 30; // 左右30px余白を考慮
const calendarY = (canvas.height / 2) / 2; // カレンダー全体の高さを280pxとして上下中央に配置
const daySize = (calendarWidth - 6 * 15) / 7; // カレンダーの幅から計算(6つの余白を考慮)
const weekdayNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
// 月と年をカレンダー中央に表示
ctx.textAlign = 'center';
ctx.fillStyle = 'black';
const calendarCenterX = calendarX + calendarWidth / 2;
const fontFamily = 'Copse';
// 年(小さく)
ctx.font = `32px ${fontFamily}`;
ctx.fillText(`${year}`, calendarCenterX, calendarY - 105);
// 月(大きく)+ 英語の月(小さく)
ctx.font = `64px ${fontFamily}`;
ctx.fillText(`${month + 1}`, calendarCenterX, calendarY - 35);
ctx.font = `28px ${fontFamily}`;
ctx.fillText(`${monthNames[month]}`, calendarCenterX, calendarY);
// 曜日ラベルを描画
ctx.font = `24px ${fontFamily}`; // 曜日のサイズをさらに小さく
for (let i = 0; i < 7; i++) {
const x = calendarX + i * (daySize + 15); // 曜日の余白調整
const y = calendarY + 55;
if (i === 0) {
ctx.fillStyle = 'rgba(255, 0, 0, 0.7)'; // 日曜日は赤
} else if (i === 6) {
ctx.fillStyle = 'rgba(0, 0, 255, 0.7)'; // 土曜日は青
} else {
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; // 平日は黒
}
ctx.textAlign = 'center'; // 曜日を中央に配置
ctx.fillText(weekdayNames[i], x + daySize / 2, y);
}
// カレンダーの日付を描画
let dayCounter = 1;
for (let i = 0; i < 6; i++) { // 最大6行
for (let j = 0; j < 7; j++) { // 7列 (曜日)
const x = calendarX + j * (daySize + 15);
const y = calendarY + 65 + i * (daySize + 15); // 日付の配置を少し下げる
if (i === 0 && j < firstDayOfMonth) {
// 1行目で月の開始前の日付は空欄に
continue;
}
if (dayCounter > daysInMonth) {
// 月末を超えたら終了
break;
}
// 日付の描画(中央寄せ)
ctx.textAlign = 'center';
if (j === 0) {
ctx.fillStyle = 'rgba(255, 0, 0, 0.7)'; // 日曜日は赤
} else if (j === 6) {
ctx.fillStyle = 'rgba(0, 0, 255, 0.7)'; // 土曜日は青
} else {
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'; // 平日は黒
}
// 選択された日付をピンクの角丸四角で囲む
if (dayCounter === day) {
drawRoundedRect(ctx, x, y - 8, daySize, 50, 10);
ctx.fillStyle = '#c43215';
ctx.fill();
ctx.strokeStyle = '#c43215';
ctx.lineWidth = 1;
ctx.stroke();
// カレンダーの数字カラーを白に
ctx.fillStyle = 'white'; // 白色に変更
}
ctx.fillText(dayCounter, x + daySize / 2, y + 25); // 日付を中央に配置
dayCounter++;
}
}
}
概要
-
Canvasの右側にカレンダーを表示し、選択した記念日を強調表示する機能
解説
-
drawCanvas()関数内で背景画像とカレンダーを描画
-
カレンダーは雑誌の表紙デザインのように、情報が上から順に整理された構成
-
年と月を大きなフォントで中央上部に表示し、視覚的に目立たせる
-
曜日はその下に横一列で配置し、曜日ごとに色分けして視認性を向上
-
日付は複数行にわたり表示し、選択された記念日は特別な色と枠で強調表示
4. デザインとレイアウト
/* CSSコード */
.canvas {
width: 800px;
height: 500px;
border: 1px solid #ccc;
border-radius: 5px;
}
.custom-label {
width: 80px;
height: 50px;
border: 1px solid #0ca0cd;
border-radius: 5px;
text-align: center;
cursor: pointer;
background-color: #f0f0f0;
}
.custom-label:hover {
background-color: #0ca0cd;
}
.download-button {
display: flex;
align-items: center;
justify-content: center;
padding: 10px 20px;
background-color: #efefef;
border-radius: 5px;
cursor: pointer;
text-decoration: none;
}
概要
-
Canvasや各種ボタンのデザインを整え、全体のレイアウトを調整する機能
解説
-
.canvasクラスでCanvasのサイズや枠線、角の丸みを設定
-
.custom-labelクラスで画像選択や日付選択ボタンのスタイルを設定
-
.download-buttonクラスでダウンロードボタンのデザインを設定
5. ダウンロード機能
// JavaScriptコード
document.getElementById('downloadButton').addEventListener('click', function() {
canvas.toBlob(function(blob) {
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = 'calendar.png';
link.click();
URL.revokeObjectURL(link.href);
}, 'image/png');
});
概要
-
Canvasに表示されたカレンダーを画像としてダウンロードできるようにする機能
解説
-
canvas.toBlob()を使用して、Canvasの内容をPNG形式のBlobに変換
-
一時的なリンクを作成し、そのリンクをクリックすることで画像をダウンロード
-
ダウンロード後、URL.revokeObjectURL()で一時的なURLを解放
まとめ
以上がChatGPTへの機能別指示内容と機能ごとのコード内容となります。
ざっくりとした機能はChatGPTが生成するコードだけで実装できましたが、余白や画像のトリミング、ボタンのデザインなどは手動で調整した部分もあります。
何か参考になれば幸いです。