Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
TypeScriptでドット絵エディタ実装録: 状態設計と実装判断
Search
Tukudani
May 23, 2026
Programming
110
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
TypeScriptでドット絵エディタ実装録: 状態設計と実装判断
https://2026.tskaigi.org/talks/49
TSKaigi 2026の10分LT登壇資料です
Tukudani
May 23, 2026
Transcript
31 slides
TypeScriptでドット絵エディタ実装録 ~状態設計と実装判断~
自己紹介
自己紹介 つくだに / 佐川 • 出身:福島県 • 所属:株式会社 オプティム フロントエンド推進室
運営メンバー • 趣味:サウナ・ゲーム・酒・ドット絵
4
5 私たちは、AI・IoT技術を社会の隅々に行き渡らせ、 日本のあらゆる産業を根底から変革する 「AXの中核を担う企業」を目指しています。
6
アジェンダ • 作ったものについて • 機能と実装判断 • まとめ 話す 話さない •
フレームワークとか、その他のこと • Canvas API の詳細 • Nuxt / Vue の詳細 • ドット絵エディタアプリ実装のロジック判断 • データ構造 • 直線・塗りつぶし • レイヤー管理 • モバイル操作の工夫
作ったもの WEBアプリ「DotArt」 • 機能 • ドット絵エディタ • ペン / 消しゴム
/ 直線 / 塗りつぶし • Undo・Redo • レイヤー機能(最大3層) • 画像保存・共有(共有機能は停止中) • モバイル・タブレット対応 • 技術スタック • Nuxt (Vue.js)
作ったもの 作った経緯 • 当時、iPhoneにモノクロ4色で簡単にドット絵が描けるアプリがあり、 とても気に入っていた • iOS11 での 32Bitアプリのサポート終了により起動不可に •
このアプリからドット絵を描き始めた人が色つきの絵を描きたくなった時に、 高機能なエディターに移行する前に触りやすい簡単なエディタを作りたかった • AIエージェントが使われだす前だったのもあり、ロジックを自前で実装していった
型システムとライフサイクルの時間軸の衝突 Nullアサーション問題 • 基本的に Canvas 要素で絵を描いているため、DOM参照が必須 • Strict モードだと初期化時は null
許容型にせざるを得ない → canvasRef.value! のような null アサーションが至る所に散らばった • nullチェックを1回行った後は、 non-nullable な変数に再アサインするのがよさそう 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型 • ドット絵エディタは座標計算だらけ • ドット絵を描く処理だけでなく、マウス操作のカーソル位置監視でも座標を受け取る • 座標情報を一元的に型指定することで広範囲の安全性を担保できた
• 小さい型定義だが、影響範囲が広いほど恩恵も大きい • 型定義のありがたみ、TSありがとう type Point = { X: number; Y: number } // 座標を受け取る関数すべてに適用 const getMousePoint = (wholeCoor: Point): Point => { ... } const figureToolsState = reactive<{ // 直線描画の状態管理 drawingFigure: Point[]; // ドラッグ中の一時表示 figureToolsStart: Point; // 直線の始点 }>({...})
基本データ構造 キャンバスデータ • ドット絵の内容と、パレットデータ等を管理する基本的な概念
基本データ構造 • 塗られた色をパレット配列のインデックスで管理 • ドット絵上の座標と1次元配列の対応は Y座標 × 横幅 + X座標
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 … 仕様
描画ツール 直線ツール • 直線を引く機能
描画ツール 実装 • ブレゼンハムのアルゴリズムを採用 • 一般的な線分描画のアルゴリズム • 直線状で横に1マス進むごとに縦に何マス進むか? を、誤差を積み上げるという手法で解決した 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; } }
描画ツール • 例: (0,0) → (6,3) の直線 (※詳細は後ほど確認ください) 横方向に 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 )
描画ツール バケツツール • 囲われた中を塗りつぶす機能
描画ツール 実装 • 再帰処理(4方向)を採用 • 特にひねりなく、別の色がぶつかるまで上下左右へ走査する 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); };
描画ツール シンプルさを重視 • どちらも一般的な方式で実装 • ドット絵のスケールでは再帰処理の呼び出し回数もそこまで多くならないので、十分と判断 o 96 × 96
程度であれば問題なく動作 実装判断
レイヤー機能 レイヤー機能 • ドット絵をレイヤー管理する機能
レイヤー機能 実装 • 最大3層、キャンバスデータを積み重ねる layerdCanvasData: { layerName: string // レイヤー名
layerIndex: number // レイヤーの重なり順 active: boolean // 表示/非表示 canvasIndexData: number[] // キャンバスデータ } canvasesIndexData: layerdCanvasData[] // 全レイヤーの配列
レイヤー機能 • ドット絵の表示処理で3層のレイヤーを下から重ねるようにすると、 何か書くたびにキャンバス × 3の比較処理が走ってしまう • 「今描いているレイヤーの上の同じ位置に色があるか」の判定があれば、 レイヤー1枚走査するだけで描画可能なのでは? 思い付き
レイヤー機能 思い付きの結果 レイヤーの合成関数→ (約150行)
レイヤー機能 • やってることはこんな感じ • 元々分岐が多い上に、 「現在描画されている最上位レイヤー」の更新処理等で複雑化している • シンプルな実装から入り、ボトルネックになってから最適化するべきだった What is
this 描画したピクセルは最前面のレイヤーか? ├─ Yes(このレイヤーが最前面) │ ├─ 背景色 かつ 下に色あり → 透過と見なす 下レイヤーの色で描画 │ ├─ 背景色 かつ 下に色なし → 背景と見なす 背景色で描画 │ └─ それ以外 → そのまま描画 └─ No(上位レイヤーが前面) → 描画スキップ、データ更新のみ
モバイル対応 ─ カーソル方式 スマホ操作モード • PC操作をそのままスマホ版に持ち込むと、 指で直接描く形になる →描いているところが指で隠れてしまう • タッチペンが無いと手を放すまでミスに気づけ
ないため、改善したい 指で隠れるので ものすごい見づらい
モバイル対応 ─ カーソル方式 実装 • 「スマホモード」として、 直接タップではなくカーソル操作形式で描画できるように
モバイル対応 ─ カーソル方式 画面内を スワイプすると カーソルも動く ボタン押下中は 描画を行う
モバイル対応 ─ カーソル方式 カーソル方式 • 既存スマホアプリで実装されており、非常に快適な操作が実現されていた • 代案が全く思いつかず、WEBで挙動を再現した! → より良い操作方式があれば知りたい
実装判断
まとめ
まとめ ・nullチェックは1箇所に集約する ・明確な課題が出ていないなら、実装はシンプルに始める ・スマホに対応するなら、操作感にこだわる 振り返って思ったこと: 「動くものを作る」と「正しく作る」は別 →動いたことに満足せず、 設計・計測・振り返りをセットにして初めて完成
ご清聴ありがとうございました! ご紹介したもの以外に興味のある機能があれば、 ぜひ懇親会等でお話ししましょう!
Other Decks in Programming
See All in Programming
AI Augmentation Techniques with Liberty
ivargrimstad
1
480
AI時代のFlutter品質設計 — アプリ品質を守るQuality Layers
omatty198
0
380
ビジネスロジックを堅牢に~Scala 3のドメインプリミティブ~
j5ik2o
0
170
AIが生成したコードは読むべきか?抽象化設計とコードの品質保証 / abstraction-design-and-quality
minodriven
16
9.2k
止まらない比較を止める — 余帰納法で無限ストリームを比べる
tarao
0
150
GraphQL の onError と Nullability のこれから
quramy
0
220
LLM 101
dalinaum
0
490
学びを記事・スライド・Xへ高速展開する― AI-assisted Learning Loopで回すコンテンツ生成術
ochtum
0
210
SODA - FACT BOOK(EN)
sodainc
1
240
作って学ぶSpring BootのAuto Configuration / JJUG Night Seminar 2026-10-19
rshindo
0
230
オーケストレーション記述言語 Katari
yukikurage
0
260
Getting Started with Vibe Coding
miccheng
0
220
Featured
See All Featured
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
203
77k
The Curious Case for Waylosing
cassininazir
1
610
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
480
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
Faster Mobile Websites
deanohume
310
32k
brightonSEO & MeasureFest 2025 - Winning Strategies for Black Friday CRO & PPC - Christian Goodrich
cargoodrich
3
900
Chasing Engaging Ingredients in Design
codingconduct
0
400
Dominate Local Search Results - an insider guide to GBP, reviews, and Local SEO
greggifford
PRO
0
400
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
230
23k
Abbi's Birthday
coloredviolet
5
11k
The Language of Interfaces
destraynor
162
27k
How STYLIGHT went responsive
nonsquared
100
6.4k