【入門者向け】Canvas入門講座#16 マウスドラッグで線を引いてみよう【JavaScript】

#問題16

マウスダウンで線の描画を開始し
マウスムーブで線を描画し
マウスアップで線の描画を終えるようにしなさい。
canvasの外にカーソルが出た場合も線の描画を終えること。

線の太さは3で色は#000000であること。

以下のHTMLを使用すること。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>問題16</title>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
$(() => {    
    $('#my-canvas').mousedown(e => {        
    });

    $('#my-canvas').mousemove(e => {        
    });

    $('#my-canvas').mouseup(e => {        
    });

    $('#my-canvas').mouseout(e => {        
    });
});
</script>
</head>
<body>
<canvas id="my-canvas" width="500" height="300"></canvas>
</body>
</html>
```
#解答

```html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>問題16</title>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
$(() => {

    let preX = 0, // 1つ前のX座標
        preY = 0, // 1つ前のY座標
        drawFlag = false;   // 線を描画中か

    $('#my-canvas').mousedown(e => {
        // canvasの左上隅を原点とする座標を求める
        const downX = e.offsetX,
            downY = e.offsetY;

        // ダウンしたので、描画フラグをtrueにする
        drawFlag = true;

        // 座標を保持する
        preX = downX;
        preY = downY;
    });

    $('#my-canvas').mousemove(e => {
        if(!drawFlag) {// 描画中でない
            return;
        }
        // canvasの左上隅を原点とする座標を求める
        const downX = e.offsetX,
            downY = e.offsetY;

        // 1つ前の座標から現在の座標まで線分を描画する
        drawLine(preX, preY, downX, downY);

        // 座標を保持する
        preX = downX;
        preY = downY;
    });

    $('#my-canvas').mouseup(e => {
        // マウスボタンが離されたので、描画フラグをfalseにする
        drawFlag = false;
    });

    $('#my-canvas').mouseout(e => {
        // マウスカーソルがcanvasの外に出たので、描画フラグをfalseにする
        drawFlag = false;
    });

    function drawLine(startX, startY, endX, endY) {
        // コンテキストを取得
        const ctx = $('#my-canvas')[0].getContext('2d');

        // 現在の描画状態を保存する
        ctx.save();   

        ctx.lineCap = 'round';
        ctx.lineWidth = 3;
        ctx.strokeStyle = '#000000';

        ctx.beginPath();				// 現在のパスをリセットする
        ctx.moveTo(startX, startY);		// パスの開始座標を指定する
    	ctx.lineTo(endX, endY);			// 座標を指定してラインを引く
        ctx.stroke();          			// 現在のパスを描画する

        // 描画状態を保存した時点のものに戻す
        ctx.restore();
    }
});
</script>
</head>
<body>
<canvas id="my-canvas" width="500" height="300"></canvas>
</body>
</html>
```

マウスドラッグで線を引くことができました。
![ダウンロード (9).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/163605/92f6c5f9-4896-b093-f3a7-72ad0ed4d4a5.png)

#解説
問題12で円をドラッグするプログラムを書きましたが、それに似ています。

使う変数は3つです。

```js
let preX = 0, // 1つ前のX座標
    preY = 0, // 1つ前のY座標
    drawFlag = false;   // 線を描画中か
```

まずはマウスダウンの処理です。
描画中フラグをtrueにして、現在のカーソルの座標を記憶します。

```js
$('#my-canvas').mousedown(e => {
    // canvasの左上隅を原点とする座標を求める
    const downX = e.offsetX,
        downY = e.offsetY;

    // ダウンしたので、描画フラグをtrueにする
    drawFlag = true;

    // 座標を保持する
    preX = downX;
    preY = downY;
});
```

次にマウスムーブの処理です。
描画中フラグがtrueのときだけ、1つ前のカーソルの座標から現在のカーソルの座標まで線分を描画します。
現在のカーソルの座標を記憶します。

今まで何かをcanvasに描画するときに毎回canvasをクリアしていましたが
<b>今回はあえてクリアしていません。</b>クリアしても描画結果は同じになるのですが、
クリアしないと以前に引いた線を描画する必要がないため、描画時間が短くなります。

```js
$('#my-canvas').mousemove(e => {
    if(!drawFlag) {// 描画中でない
        return;
    }
    // canvasの左上隅を原点とする座標を求める
    const downX = e.offsetX,
        downY = e.offsetY;

    // 1つ前の座標から現在の座標まで線分を描画する
    drawLine(preX, preY, downX, downY);

    // 座標を保持する
    preX = downX;
    preY = downY;
});

function drawLine(startX, startY, endX, endY) {
    // コンテキストを取得
    const ctx = $('#my-canvas')[0].getContext('2d');

    // 現在の描画状態を保存する
    ctx.save();   

    ctx.lineCap = 'round';
    ctx.lineWidth = 3;
    ctx.strokeStyle = '#000000';

    ctx.beginPath();				// 現在のパスをリセットする
    ctx.moveTo(startX, startY);		// パスの開始座標を指定する
	ctx.lineTo(endX, endY);			// 座標を指定してラインを引く
    ctx.stroke();          			// 現在のパスを描画する

    // 描画状態を保存した時点のものに戻す
    ctx.restore();
}
```

マウスアップで描画フラグもfalseにして、描画を終了します。
マウスアウトでも同様です。

```js
$('#my-canvas').mouseup(e => {
    // マウスボタンが離されたので、描画フラグをfalseにする
    drawFlag = false;
});

$('#my-canvas').mouseout(e => {
    // マウスカーソルがcanvasの外に出たので、描画フラグをfalseにする
    drawFlag = false;
});
```