オウルです。
今回は、意外に奥が深く、知っておくと便利なドラッグ&ドロップ。
理解を深めるためにイメージを Canvas にドラッグ&ドロップして、そのイメージを使って ”ぬりえ” や ”お絵かき” ができるように JavaScript で実装していきます。
イメージを Canvas にドラッグ & ドロップ
まず、ドラッグ & ドロップのイベントを見てみましょう。
Drag Event
- dragend
- dragenter
- dragexit
- dragleave
- dragover
- dragstart
- drop
上記の Drag Event の中で、今回使用するのは「dragover」と「drop」の2つです。
dragover
dragover は、ドラッグが有効なドロップ先の上を通過中に発生するイベントです。
drop
drop は、有効なドロップ先にドロップされたときに発生するイベントです。
HTML CSS
HTML
1 2 3 |
<div id="dnd"> <canvas id="dnd-canvas"></canvas> </div> |
CSS
1 2 3 4 5 |
#dnd { width: 320px; height: 240px; background-color: rgb(189, 243, 235); } |
JavaScript
ローカル環境
ブラウザ: Google Chrome
javascript: ES2015~
dragoverハンドラ定義
リスナーを適切に削除できるように、無名関数を使わず、ここでは handleEvent() 関数を使用します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
const dragOverHandler = { handleEvent: (event) => { // drop 側に”copy”を許可します // none: ドロップを禁止 // copy: コピーのみ許可 // move: 移動のみ許可 // link: リンクのみ許可 // copyMove: コピー/移動のみ許可 // copyLink: コピー/リンクのみ許可 // linkMove: リンク/移動のみ許可 // all: 全ての操作が許可 event.dataTransfer.dropEffect = 'copy'; // dragenter および dragover イベントに対する既定動作はドロップ禁止です。 // この規定動作を無効化するため、preventDefault() メソッドを呼び出し、その場所をドロップ可能領域とします。 event.preventDefault(); } }; target.addEventListener('dragover', dragOverHandler, { capture: false, once: false, passive: false }); |
試しに Chrome DevTools を使ってイベントの取得と削除を確認してみます。
1 2 3 |
const dnd = document.getElementById('dnd'); // Chrome DevTools コマンドライン API getEventListeners(dnd); |

1 2 |
dnd.removeEventListener('dragover', dragOverHandler); getEventListeners(dnd); |

適切に削除されています。
dropハンドラ定義
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
const dropHandler = { handleEvent: (event) => { // ドラッグしたファイル const files = event.dataTransfer.files; // 今回は1ファイルのみ if (files.length === 1) { const file = files[0]; // FileReaderは、ファイル (もしくはバッファ上の生データ) を非同期的に読み込むオブジェクト const reader = new FileReader(); reader.addEventListener('load', loadReaderHandler, { capture: false, once: true, passive: false }); // 指定された Blob ないし File オブジェクトを読み込み、 // result プロパティにはファイルのデータを表す、base64 エンコーディングされた data: URL の文字列が格納される reader.readAsDataURL(file); } // ブラウザ内でのドロップ時の既定の挙動をキャンセル // キャンセルしないとブラウザにイメージが表示される event.preventDefault(); } } const drawCanvasHandler = { handleEvent: (event) => { const img = event.target; let canvas = document.getElementById('dnd-canvas'); let ctx = canvas.getContext('2d'); canvas.width = 320; canvas.height = 240; ctx.clearRect(0, 0, canvas.width, canvas.height); // イメージのリサイズ ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, 320, 240); ctx.save(); } }; const loadReaderHandler = { handleEvent: (event) => { const img = new Image(); // イメージの読み込み完了後にdrawImage()を呼ぶ img.addEventListener('load', drawCanvasHandler, { capture: false, once: true, passive: true }); // FileReaderの結果(Data URL)をセット img.src = event.target.result; } }; target.addEventListener('drop', dropHandler, { capture: false, once: false, passive: false }); |
ファイルの result プロパティの補足
ファイルの result は、直接 Base64 としてデコードできない文字列を返します。 Base64 でエンコードされた文字列のみを受け取る場合は、文字列から data:*/*;base64, を削除する必要があります。
動作確認(Code Pen)
See the Pen
RwNgrpE by owl (@blue-owl)
on CodePen.
次回は、イメージを設定した Canvas 上で ”ぬりえ” や ”お絵かき” ができるようにします。