9
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Canvasで画像を描画する

Last updated at Posted at 2024-07-29

はじめに

Canvas API を使用すると、ブラウザ上で画像やテキストを描画できるのですが、画像を切り取ったり、拡大・縮小したり、マウスクリックした状態でドラッグして移動したり...
といった色々なことができます。

弊社のいくつかのプロダクトでは、これらの技術を用いて合成画像を出力し、お客さんへ届けるという仕組みを構築しています。

本記事では、Canvas で画像を描画するための、非常に基本的な使い方を紹介します。

まずは画像を描画する

canvas要素は、二次元描画コンテキストを提供します。
これは、図形、文字、画像、その他のオブジェクトを描画するのに使用します。

今回、横と縦が300 * 400のねこちゃん画像を描画します。
描画される範囲(枠)のサイズは広めの800 * 600としておきます。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Canvasで画像を描画</title>
  <style>
    canvas {
      border: 1px solid black;
    }
  </style>
</head>
<body>
  <canvas id="myCanvas" width="800" height="600"></canvas>
  <script src="script.js"></script>
</body>
</html>
script.js
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

const image = new Image();
image.src = 'image.jpg';

image.onload = () => {
  ctx.drawImage(image, 0, 0);
};

スクリーンショット 2024-07-26 18.40.33.png

ねこちゃん画像がそのままのサイズで枠内に描画されました。

画像のサイズを指定して描画

横と縦を1/2のサイズにしてみましょう。

//画像を指定した横幅と高さで描画。
ctx.drawImage(image, 0, 0, 150, 200);

スクリーンショット 2024-07-26 18.50.53.png

小さくなりました。

画像の一部を切り取って描画

300 * 400の元画像のうち、150 * 200の範囲だけを切り取ってみましょう。

// 元画像に対して、(0,0)の位置から150 * 200の範囲を切り取る。引数2~5までの値指定。
// 切り取った画像を、(0,0)の位置から150 * 200の範囲で描画する。引数6~9までの値指定。
ctx.drawImage(image, 0, 0, 150, 200, 0, 0, 150, 200); 

スクリーンショット 2024-07-26 18.58.09.png

左目だけ描画されました。

画像の中心を Canvas の中心に合わせて描画

image.onload = () => {
  const canvasWidth = canvas.width;
  const canvasHeight = canvas.height;
  const imageWidth = image.width;
  const imageHeight = image.height;

  const x = (canvasWidth - imageWidth) / 2;
  const y = (canvasHeight - imageHeight) / 2;

  ctx.drawImage(image, x, y);
};

こうなります。

スクリーンショット 2024-07-26 19.17.48.png

つまり、左上の赤丸の点の座標から描画すればOKということです。

スクリーンショット 2024-07-26 19.17.48.png

画像をCanvasの枠に合わせて拡大する

少し応用編です。
画像をCanvasの枠に合わせて拡大する場合、アスペクト比を維持することが重要です。
以下の関数を使用して、画像を Canvas の幅または高さに合わせて拡大します。

const adjustSizeByWidth = (canvasWidth, imageAspectRatio, scale) => {
  const drawWidth = canvasWidth * scale;
  const drawHeight = drawWidth / imageAspectRatio;
  return { drawWidth, drawHeight };
};

const adjustSizeByHeight = (canvasHeight, imageAspectRatio, scale) => {
  const drawHeight = canvasHeight * scale;
  const drawWidth = drawHeight * imageAspectRatio;
  return { drawWidth, drawHeight };
};

const calculateImageSize = (imageWidth, imageHeight, canvasWidth, canvasHeight, scale = 1.0) => {
  const imageAspectRatio = imageWidth / imageHeight;
  const canvasAspectRatio = canvasWidth / canvasHeight;
  if (imageAspectRatio > canvasAspectRatio) {
    return adjustSizeByWidth(canvasWidth, imageAspectRatio, scale);
  } else {
    return adjustSizeByHeight(canvasHeight, imageAspectRatio, scale);
  }
};

実際に描画する箇所の処理はこうなります。

image.onload = () => {
  const { drawWidth, drawHeight } = calculateImageSize(image.width, image.height, canvas.width, canvas.height);
  const x = (canvas.width - drawWidth) / 2;
  const y = (canvas.height - drawHeight) / 2;
  ctx.drawImage(image, x, y, drawWidth, drawHeight);
};

スクリーンショット 2024-07-26 19.29.57.png

今回は、横長の枠に対して縦長の画像を描画する形ですので、画像は枠に対して縦合わせになりました。

画像の拡大・縮小

HTML の input 要素を使用して、画像の拡大・縮小を実装してみましょう。
以下記述を追加します。

<label for="zoom">Zoom: </label>
<input type="range" id="zoom" min="0.1" max="3" step="0.1" value="1">

JavaScript で input 要素の値を監視し、画像のスケールを変更します。
input要素の値の変更があればその都度描画します。
ただ注意点として、描画する前にclearRectでCanvasの描画状態を消しましょう。
そうしないと前回の描画が残った状態で次の描画を行なってしまいます。

script.js
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
let scale = 1;

const drawImage = () => {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  const drawWidth = image.width * scale;
  const drawHeight = image.height * scale;
  const x = (canvas.width - drawWidth) / 2
  const y = (canvas.height - drawHeight) / 2
  ctx.drawImage(image, x, y, drawWidth, drawHeight);
};

const zoomInput = document.getElementById('zoom');
zoomInput.addEventListener('input', (event) => {
  scale = event.target.value;
  drawImage();
});

const image = new Image();
image.src = 'image.png';

image.onload = () => {
  drawImage();
};
mojikyo45_640-2.gif

画像の移動

マウスイベントを使用して、画像をドラッグして移動させることができます。

script.js
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

let offsetX = 0;
let offsetY = 0;
let isDragging = false;
let startX, startY;

const drawImage = () => {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  const drawWidth = image.width
  const drawHeight = image.height
  const x = (canvas.width - drawWidth) / 2 + offsetX;
  const y = (canvas.height - drawHeight) / 2 + offsetY;
  ctx.drawImage(image, x, y, drawWidth, drawHeight);
};

canvas.addEventListener('mousedown', (event) => {
  isDragging = true;
  startX = event.offsetX - offsetX;
  startY = event.offsetY - offsetY;
});

canvas.addEventListener('mousemove', (event) => {
  if (isDragging) {
    offsetX = event.offsetX - startX;
    offsetY = event.offsetY - startY;
    drawImage();
  }
});

canvas.addEventListener('mouseup', () => {
  isDragging = false;
});

canvas.addEventListener('mouseleave', () => {
  isDragging = false;
});

const image = new Image();
image.src = 'image.png';

image.onload = () => {
  drawImage();
};
mojikyo45_640-2.gif

最後に

次回は、また別の処理について記事を書く予定をしています。
それではまた〜。

9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?