これを作った時の知見を忘れないうちにメモ
getImageDataとputImageData
canvasのcontext.getImageData()を用いることで、描画されているラスタのデータを範囲指定してピクセルの配列(ImageData)として取得することができます。
また、取得したImageDataはputImageData()で描画を行うことができます。
ImageDataをスタックとして変数に保持しておけば元に戻す・やり直すという動作を実現させることができます。
それぞれの配列の挙動はFIFOになるように実装します。
サンプルコード
let $canvas = $('#myCanvas');
// contextを取得
let ctx = $canvas[0].getContext('2d');
// スタックしておく最大回数。キャンバスの大きさの都合などに合わせて調整したら良いです。
const STACK_MAX_SIZE = 5;
// スタックデータ保存用の配列
let undoDataStack = [];
let redoDataStack = [];
// canvasへの描画処理を行う前に行う処理
function beforeDraw() {
// やり直し用スタックの中身を削除
redoDataStack = [];
// 元に戻す用の配列が最大保持数より大きくなっているかどうか
if (undoDataStack.length >= STACK_MAX_SIZE) {
// 条件に該当する場合末尾の要素を削除
undoDataStack.pop();
}
// 元に戻す配列の先頭にcontextのImageDataを保持する
undoDataStack.unshift(ctx.getImageData(0, 0, $canvas[0].width(), $canvas[0].height()));
}
function undo () {
// 戻す配列にスタックしているデータがなければ処理を終了する
if (undoDataStack.length <= 0) return;
// やり直し用の配列に元に戻す操作をする前のCanvasの状態をスタックしておく
redoDataStack.unshift(ctx.getImageData(0, 0, $canvas[0].width(), $canvas[0].height()));
// 元に戻す配列の先頭からイメージデータを取得して
var imageData = undoDataStack.shift();
// 描画する
ctx.putImageData(imageData, 0, 0);
}
function redo () {
// やり直し用配列にスタックしているデータがなければ処理を終了する
if (redoDataStack.length <= 0) return;
// 元に戻す用の配列にやり直し操作をする前のCanvasの状態をスタックしておく
undoDataStack.unshift(ctx.getImageData(0, 0, $canvas[0].width(), $canvas[0].height()));
// やり直す配列の先頭からイメージデータを取得して
var imageData = redoDataStack.shift();
// 描画する
ctx.putImageData(imageData, 0, 0);
}
関連リファレンス
https://developer.mozilla.org/ja/docs/Web/API/CanvasRenderingContext2D/getImageData
https://developer.mozilla.org/ja/docs/Web/API/CanvasRenderingContext2D/putImageData
https://developer.mozilla.org/ja/docs/Web/API/ImageData