#問題10
中心が(150, 150)、半径50の円を塗りつぶしなさい。
塗りつぶす色はマゼンタ色(#ff00ff)であること。
rightボタン押下時に円がX方向へ-30,leftボタン押下時に円がX方向へ+30移動すること。
なお、以下のHTMLを使うこと。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>問題10</title>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
$(() => {
// ここにプログラムを書く
});
</script>
</head>
<body>
<canvas id="my-canvas" width="500" height="300"></canvas>
<br>
<input id="left-button" type="button" value="right" />
<input id="right-button" type="button" value="left" />
</body>
</html>
#答え
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>問題10</title>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
$(() => {
let moveX = 0;
// 初期表示
drawCircle(moveX);
$('#left-button, #right-button').on('click', e => {
if($(e.target).prop('id') === 'left-button') {// left-button がクリックされた
moveX -= 30;
} else {// right-button がクリックされた
moveX += 30;
}
drawCircle(moveX);
});
function drawCircle(moveX) {
// コンテキストを取得
const ctx = $('#my-canvas')[0].getContext('2d');
// 現在の描画状態を保存する
ctx.save();
// canvasをクリア
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// 塗りつぶしの色をマゼンタ色にする
ctx.fillStyle = '#ff00ff';
ctx.beginPath(); // 現在のパスをリセットする
ctx.arc(150 + moveX, 150, 50, 0, Math.PI*2, true); // 円を描画する
ctx.closePath(); // パスを閉じる
ctx.fill(); // 現在のパスを塗りつぶす
// 描画状態を保存した時点のものに戻す
ctx.restore();
}
});
</script>
</head>
<body>
<canvas id="my-canvas" width="500" height="300"></canvas>
<br>
<input id="left-button" type="button" value="left" />
<input id="right-button" type="button" value="right" />
</body>
</html>
#解説
円がどれだけ動いたか変数で保持します。
let moveX = 0;
まず円を描画します。今回は円の描画を関数にしてあります。
// 初期表示
drawCircle(moveX);
ボタンクリックをまとめてハンドリングします。
left-button がクリックされたら、moveXを-30します。
right-button がクリックされたら、moveXを+30します。
$('#left-button, #right-button').on('click', e => {
if($(e.target).prop('id') === 'left-button') {// left-button がクリックされた
moveX -= 30;
} else {// right-button がクリックされた
moveX += 30;
}
drawCircle(moveX);
});
drawCircleは円を描画する関数です。
引数はmoveXでX方向にどれだけ移動したかを受け取ります。
canvasへ描画する前にclearRectメソッドでcanvasをクリアしています。
これはクリアしないと1つ前の円の描画そのまま残るのを防ぐためです。
clearRectの代わりにfillRectによる塗りつぶしでも構いません。
一般的にcanvasに何かを描画するときは、canvasをクリアした後に、canvasへ描画します。
あと、描画する前にsaveメソッドを、描画が完了した後にはrestoreメソッドを呼ぶようにしたほうが良いです。
saveメソッドは描画情報(塗りつぶしや線の色など)を保存し、restoreメソッドはそれを復元します。
function drawCircle(moveX) {
// コンテキストを取得
const ctx = $('#my-canvas')[0].getContext('2d');
// 現在の描画状態を保存する
ctx.save();
// canvasをクリア
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// 塗りつぶしの色をマゼンタ色にする
ctx.fillStyle = '#ff00ff';
ctx.beginPath(); // 現在のパスをリセットする
ctx.arc(150 + moveX, 150, 50, 0, Math.PI*2, true); // 円を描画する
ctx.closePath(); // パスを閉じる
ctx.fill(); // 現在のパスを塗りつぶす
// 描画状態を保存した時点のものに戻す
ctx.restore();
}
#補足
setTransformを使うと変数を描画処理で参照しなくてよいのですっきりします。
今回の問題は描画が単純ですので、このような工夫は必要ありませんが
setTransformは知っててほしいメソッドです。
setTransformは行列を掛けるメソッドです。
平行移動する場合はctx.setTranform(1, 0, 0, 0, x, y);のように呼び出しましょう。
今回のCanvas入門講座ではオブジェクトの変換は平行移動しかしないので、
setTransformの代わりにtranslateでもよいです。
setTranformについて詳しく知りたい方はこちらの記事をお読みください。
以下、drawCircleをsetTransformを使って書き直しました。
function drawCircle(moveX) {
// コンテキストを取得
const ctx = $('#my-canvas')[0].getContext('2d');
// 現在の描画状態を保存する
ctx.save();
// canvasをクリア
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// 塗りつぶしの色をマゼンタ色にする
ctx.fillStyle = '#ff00ff';
ctx.setTransform(1, 0, 0, 1, moveX, 0);
ctx.beginPath(); // 現在のパスをリセットする
ctx.arc(150, 150, 50, 0, Math.PI*2, true); // 円を描画する
ctx.closePath(); // パスを閉じる
ctx.fill(); // 現在のパスを塗りつぶす
// 描画状態を保存した時点のものに戻す
ctx.restore();
}