こうこく
作 ▸
改 ▸

Canvasでペイントみたいにドット絵を描くサンプル

一応、PC・タッチの両操作に対応してます。

PC・タッチ操作対応と書きましたが、残念ながらマルチタッチは考慮してません。本当は identifier 見て、描画開始した指と同じ指が離れたときに描画終了するべきだと思います。

描画色の選択は <input type="color" /> 使ってみました。でも対応してないブラウザで見ると、コード値を直接入力させられると思います。

あと、線の補間はしてないので、速く動かすと線がぶちぶち切れます。

See the Pen drawing-pixelart-with-canvas by napoporitataso (@napoporitataso) on CodePen.

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="UTF-8" />
	<meta name="robots" content="noarchive" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
	<meta name="landscape" content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
	<style>
html {
  background-color:#000;
  color:#eee;
}
#pixel-canvas {
  margin: 5px 0 10px;
  border: 1px solid #666;
}
	</style>
	<title>Canvasドット絵 サンプル</title>
</head>
<body>
	描画色: <input type="color" id="brush-color" value="#ffffff" /><br />
	<canvas id="pixel-canvas"></canvas>
	<script src="script.js"></script>
</body>
</html>
script.js
const PIXEL_SIZE = 8; // 1ドットの大きさ
const CANVAS_WIDTH = PIXEL_SIZE * 32; // キャンバス横幅
const CANVAS_HEIGHT = PIXEL_SIZE * 32; // キャンバス縦幅

const canvas = document.getElementById('pixel-canvas');
const _ctx = canvas.getContext('2d');
let _drawing = false;

/**
 * MouseEventまたはTouchからCanvas内での座標を算出して返却
 * @param {MouseEvent|Touch}
 * @return {{x: number; y: number}} x: x座標, y: y座標
 */
const getEventXY = (e) => {
  const bounds = e.target.getBoundingClientRect();
  return {
    x: e.clientX - bounds.left,
    y: e.clientY - bounds.top,
  };
};

/**
 * RGBを指定してコンテキストに描画色を設定する
 * @param {number} R
 * @param {number} G
 * @param {number} B
 */
const setBrushColor = (r, g, b) => {
  _ctx.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ', 1)';
};

/**
 * Canvas内の座標を指定して、描画モードONならドットを描画する
 * @param {number} x座標
 * @param {number} y座標
 */
const draw = (x, y) => {
  if (_drawing) {
    // ドットの左上の座標を算出
    const upperLeftX = Math.floor(((x / canvas.width) * CANVAS_WIDTH) / PIXEL_SIZE) * PIXEL_SIZE;
    const upperLeftY = Math.floor(((y / canvas.height) * CANVAS_HEIGHT) / PIXEL_SIZE) * PIXEL_SIZE;

    // ドットの大きさの矩形を描画
    _ctx.fillRect(upperLeftX, upperLeftY, PIXEL_SIZE, PIXEL_SIZE);
  }
};

// Canvasの幅と高さを設定
canvas.width = CANVAS_WIDTH;
canvas.height = CANVAS_HEIGHT;

// 不透明の黒で塗りつぶす
setBrushColor(0, 0, 0);
_ctx.fillRect(0, 0, canvas.width, canvas.height);

// 初期描画色を白に
setBrushColor(255, 255, 255);

// 左クリックで描画モードON + 1発目の描画
canvas.addEventListener('mousedown', (e) => {
  if (e.button <= 1) {
    _drawing = true;
    const { x, y } = getEventXY(e);
    draw(x, y);
  }
});
// タップ開始で同上
canvas.addEventListener('touchstart', (e) => {
  // ※マルチタッチ考慮せず
  _drawing = true;
  const { x, y } = getEventXY(e.changedTouches[0]);
  draw(x, y);
});

// 左クリックで描画モードOFF
canvas.addEventListener('mouseup', (e) => {
  if (e.button <= 1) {
    _drawing = false;
  }
});
// タップ終了で同上 (※マルチタッチ考慮せず)
canvas.addEventListener('touchend', () => {
  _drawing = false;
});

// カーソル移動で描画
canvas.addEventListener('mousemove', (e) => {
  const { x, y } = getEventXY(e);
  draw(x, y);
});
// 指移動で同上 (※マルチタッチ考慮せず)
canvas.addEventListener('touchmove', (e) => {
  e.preventDefault(); // スクロール防止
  const { x, y } = getEventXY(e.changedTouches[0]); // 1本目の指だけ使う
  draw(x, y);
});

// 描画色変更
document.getElementById('brush-color').addEventListener('change', (e) => {
  const code = e.target.value;
  const r = parseInt(code.substr(1, 2), 16);
  const g = parseInt(code.substr(3, 2), 16);
  const b = parseInt(code.substr(5, 2), 16);
  setBrushColor(r, g, b);
});
この記事に何かあればこちらまで (非公開)