TypeScriptでドット絵エディタ実装録
~状態設計と実装判断~
自己紹介
自己紹介
つくだに / 佐川
4
私たちは、AI・IoT技術を社会の隅々に行き渡らせ、
日本のあらゆる産業を根底から変革する
「AXの中核を担う企業」を目指しています。
5
6
アジェンダ
✔ 話す
✖ 話さない
作ったもの
WEBアプリ「DotArt」
作ったもの
作った経緯
型システムとライフサイクルの時間軸の衝突
Nullアサーション問題
const canvas = document.querySelector<HTMLCanvasElement>('#drawcanvas');
const ctx = canvas?.getContext('2d');
if (!canvas || !ctx) throw new Error('canvas init failed’);
canvasState.canvas = canvas;
canvasState.canvasCtx = ctx;
Point型 ── 座標を守る型
Point型
type Point = { X: number; Y: number }
�// 座標を受け取る関数すべてに適用
const getMousePoint = (wholeCoor: Point): Point => { ... }
const figureToolsState = reactive<{ // 直線描画の状態管理� drawingFigure: Point[]; // ドラッグ中の一時表示� figureToolsStart: Point; // 直線の始点�}>({...})
基本データ構造
キャンバスデータ
基本データ構造
canvasIndexData[ Y × N + X ] = paletteIndex
0
9
9
9
9
9
9
9
0
0
8
8
8
8
0
0
0
0
0
0
9
0
0
0
0
8
0
0
0
0
8
0
…
…
[ Y × 0 + X ]
[ Y × 1 + X ]
…
0
1
2
3
4
5
6
7
8
9
…
仕様
描画ツール
直線ツール
描画ツール
実装
let e = -xdiff;
for (let i = 0; i <= xdiff; i++) {
Line.push({ ...cell });
cell.X += Xvek;
e += ydiff2;
if (e >= 0) { cell.Y += Yvek; e -= xdiff2; }
}
描画ツール
横方向に 7 , 縦方向に 3 マス進む�↓
閾値 = 7(横の距離)
加算量 = 3(縦の距離)�横移動のたびに誤差 += 3 を積み上げ、
誤差が 7 以上になったら縦に1マス進み、誤差 -= 7 する
X=0: 誤差= 3 → 7未満 → Y=0 ・・・( 0, 0 )
X=1: 誤差= 6 → 7未満 → Y=0 ・・・( 1, 0 )
X=2: 誤差= 9 → 7以上! -7 → Y=1 ・・・( 2, 1 )
X=3: 誤差= 5 → 7未満 → Y=1 ・・・ ( 3, 1 )
X=4: 誤差= 8 → 7以上! -7 → Y=2 ・・・ ( 4, 2 )
X=5: 誤差= 4 → 7未満 → Y=2 ・・・ ( 5, 2 )
X=6: 誤差= 7 → 7以上! -7 → Y=3 ・・・ ( 6, 3 )
描画ツール
バケツツール
描画ツール
実装
const fill = (cell, color, target) => {
if (範囲外) return;
if (target[cell.Y * N + cell.X] !== color) return;
drawDot(cell);
fill({ X: cell.X - 1, Y: cell.Y }, color, target);
fill({ X: cell.X + 1, Y: cell.Y }, color, target);
fill({ X: cell.X, Y: cell.Y - 1 }, color, target);
fill({ X: cell.X, Y: cell.Y + 1 }, color, target);
};
描画ツール
シンプルさを重視
実装判断
レイヤー機能
レイヤー機能
レイヤー機能
実装
layerdCanvasData: {
layerName: string // レイヤー名
layerIndex: number // レイヤーの重なり順
active: boolean // 表示/非表示
canvasIndexData: number[] // キャンバスデータ
}
canvasesIndexData: layerdCanvasData[] // 全レイヤーの配列
レイヤー機能
🤔
思い付き
レイヤー機能
思い付きの結果
🤔
レイヤーの合成関数→�(約150行)
レイヤー機能
What is this
描画したピクセルは最前面のレイヤーか?
├─ Yes(このレイヤーが最前面)
│ ├─ 背景色 かつ 下に色あり → 透過と見なす 下レイヤーの色で描画
│ ├─ 背景色 かつ 下に色なし → 背景と見なす 背景色で描画
│ └─ それ以外 → そのまま描画
└─ No(上位レイヤーが前面) → 描画スキップ、データ更新のみ
モバイル対応 ─ カーソル方式
スマホ操作モード
指で隠れるので�ものすごい見づらい
モバイル対応 ─ カーソル方式
実装
モバイル対応 ─ カーソル方式
画面内を�スワイプすると
カーソルも動く
ボタン押下中は
描画を行う
モバイル対応 ─ カーソル方式
カーソル方式
実装判断
まとめ
まとめ
・nullチェックは1箇所に集約する
・明確な課題が出ていないなら、実装はシンプルに始める
・スマホに対応するなら、操作感にこだわる
振り返って思ったこと:
「動くものを作る」と「正しく作る」は別�
→動いたことに満足せず、
設計・計測・振り返りをセットにして初めて完成
ご清聴ありがとうございました!
ご紹介したもの以外に興味のある機能があれば、�ぜひ懇親会等でお話ししましょう!