1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Canvas要素でカレンダーアプリを作ってみる#1

Last updated at Posted at 2024-12-05

はじめに

PixiJSとCanvas要素を使用してカレンダーアプリを作りたくなったので作ってみる。(全3記事を予定)

(Canvas内で行わなくても普通にDOMでも作ることは可能ですが、PixiJSの勉強もかねています🧑‍🎨)

PixiJSとは、(下記URLより)

PixiJS は本質的に、WebGL (またはオプションで Canvas) を使用して画像やその他の 2D ビジュアル コンテンツを表示するレンダリング システムです。

PixiJS は、完全なシーン グラフ (レンダリングするオブジェクトの階層) を提供し、

クリック イベントやタッチ イベントを処理できるようにするインタラクション サポートを提供します。

今回作るカレンダーアプリ

こんな感じのアプリを作成する。

主な機能は

  • Calendar表示

Canvas要素でカレンダーアプリを作ってみる1.jpg

  • D&D(Drag And Drop)による登録・移動

Canvas要素でカレンダーアプリを作ってみる2.gif

  • 時間軸のResize

Canvas要素でカレンダーアプリを作ってみる3.gif

  • Taskの重複の警告

Canvas要素でカレンダーアプリを作ってみる4.gif

  • Title・Colorの変更

Canvas要素でカレンダーアプリを作ってみる5.gif

  • HoverTitle

Canvas要素でカレンダーアプリを作ってみる6.gif

  • LocalStrage保存機能

Canvas要素でカレンダーアプリを作ってみる7.gif

成果物DEMO_URL

今回使用するライブラリなど

環境作成

ViteのVanillaのTemplateをベースに作成する。

今回は、プロジェクト名は my-calendar で作成する。

npm create vite@latest my-calendar -- --template vanilla

プロジェクトの作成

htmlとcssを用いて画面を作成する。

画面上部には表示時の年月と左右のページ送りボタンとテキスト入力とカラー指定のInput、Taskの更新ボタンと削除ボタンを配置する。

index.html
<!doctype html>
<html lang="ja">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <link rel="stylesheet" href="style.css" />
  <title>mycalendar</title>
</head>

<body>
  <div class="row">
    <button autofocus id="arrowleft"></button>
    <div id="calendar_month"></div>
    <button id="arrowright"></button>
  </div>
  <div class="row">
    <input type="color" id="taskcolor">
    <input type="text" id="tasktitle" maxlength="30">
    <button id="taskchange" data-taskid="">TASK CHANGE</button>
    <button id="taskdelete" data-taskid="">TASK DELETE</button>
  </div>
  <div class="row">
    <button id="calendarsave" data-taskid="">CALENDAR SAVE</button>
  </div>
  <div id="gr">
    <canvas id="padcanvas"></canvas>
    <canvas id="headcanvas"></canvas>
    <canvas id="leftcanvas"></canvas>
    <div id="appmain"></div>
  </div>
  <script type="module" src="/main.js"></script>
</body>

</html>

Canvas内のCalendar上の罫線はPixiJSで引いても良いが、

より簡単にCSSのbackground-imageやbackground-sizeを使用して罫線を引いておく。

style.css
body {
    background-color: #000;
    margin: 0;
    padding: 0;
    font-family: monospace;
    font-size: 12px;
    color: lightgray;
}

.row {
    display: flex;
    align-items: center;
    margin: 4px;
}

#arrowright,
#arrowleft,
#taskchange,
#taskdelete {
    font-size: 10px;
    height: 20px;
}


canvas {
    display: block;
    border: 1px solid lightgray;
    background-image:
        linear-gradient(to right, lightgray 1px, transparent 1px),
        linear-gradient(to bottom, lightgray 1px, transparent 1px);
}

#gr {
    display: grid;
    grid-template-columns: 60px 1440px;
    width: 100%;
}

#padcanvas {
    background-color: black;
    height: 20px;
    width: 60px;
    background-size: auto;
    position: sticky;
    left: 0;
}

#headcanvas {
    height: 20px;
    width: 1440px;
    background-size: auto;
}

#leftcanvas {
    background-color: black;
    position: sticky;
    left: 0;
    height: 1240px;
    background-size: 60px 40px;
}

#timemaincanvas {
    height: 1240px;
    background-size: 60px 40px;
    left: 60px;
}

windowのLoadイベントでPGliteを使用して

local上に保持しておきたい、task用のテーブルをなければ、Create。

カレンダーの上部の時間軸を表示し、

task操作を行うCanvas内はPixiJSのapp.initで初期設定しておく。

main.js
// 略
const db = new PGlite('idb://my-pgdata');

const app = new PIXI.Application();
const cellWidth = 60;
const cellHeight = 40;
const objh = cellHeight / 2;
const padtoph = cellHeight / 4;
const maxtime = 24;
let maxDays = 31;
let cellHeightCount = maxDays;
let gridHeight = cellHeight * cellHeightCount;

const calendarsave = document.getElementById('calendarsave');
const taskdelete = document.getElementById('taskdelete');
const taskchange = document.getElementById('taskchange');
const tasktitle = document.getElementById('tasktitle');
const taskcolor = document.getElementById('taskcolor');
const calendar_month = document.getElementById('calendar_month');
const padcanvas = document.getElementById('padcanvas');
const leftcanvas = document.getElementById('leftcanvas');
const headcanvas = document.getElementById('headcanvas');

let createStart = { x: -1, y: -1 };
let draggingTask = false;
let resizingTask = false;

const _Graphics = '_Graph';
const _ResizeL = '_ResizeL';
const _ResizeR = '_ResizeR';
const _Distinct = '_Distinct';
const _Text = '_Text';

const tempObjLabel = 'tempObjLabel';
const tempMoveObjLabel = 'tempMoveObjLabel';
const focusGraphics = 'focusGraph';

const defaulttitle = 'Empty!';
const defaultcolor = '#ffa500';

window.onload = async () => {
  await db.query(`
  CREATE TABLE IF NOT EXISTS task_master (
    taskid UUID PRIMARY KEY NOT NULL,
    title VARCHAR(30) NOT NULL,
    color CHAR(7) NOT NULL,
    x DECIMAL(4, 0) NOT NULL,
    y DECIMAL(4, 0) NOT NULL,
    width DECIMAL(4, 0) NOT NULL,
    yearmonth DECIMAL(6, 0) NOT NULL );`)
  const dt = new Date();
  maxDays = monthDays(dt);
  gridHeight = cellHeight * maxDays;
  calendar_month.innerText = format({ date: dt, format: 'YYYY-MM', tz: 'Asia/Tokyo', });
  // ヘッダー部の初期設定
  padcanvas.height = cellHeight / 2;
  padcanvas.width = cellWidth;
  // ヘッダーに時間を記載
  headcanvas.height = cellHeight / 2;
  headcanvas.width = cellWidth * maxtime;
  const headctx = headcanvas.getContext('2d');
  headctx.font = `15px monospace`;
  headctx.fillStyle = 'lightgray';
  let x = 0;
  for (let i = 1; i < maxtime; i++) {
    x += cellWidth
    const buffer = i < 10 ? 3 : 5;
    headctx.fillText(i, x - buffer, 15);
  }
  // カレンダー 初期設定
  await app.init({
    width: cellWidth * maxtime,
    height: gridHeight,
    backgroundAlpha: 0
  });
  app.canvas.id = 'timemaincanvas';
  app.stage.interactive = true;
  app.stage.hitArea = app.renderer.screen;
  document.getElementById('appmain').appendChild(app.canvas);
}

// 略

まとめ

今回は、構成の説明と画面の初期設定まで行った。

次回はカレンダーアプリ内のTask登録やインタラクティブな操作のロジックを記事にしていく。

次回につづく。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?