雑食性雑感雑記

知識の整理場。ため込んだ知識をブログ記事として再構築します。

【JavaScript & Canvas】 アレコレ作るための下地クラスを整備

前回、JavaScript の復習と仕事の復習かねて Canvas やってたら楽しかったので、もう少しアレコレ作ってみる。

――その前に、アニメーションとかやろうとするとコードがごちゃごちゃになるのは目に見えているので、
その辺を整理する。

作成物

今回は特に工夫は無く、
アニメーションで■が左上から右下に移動するだけです。

技術要素

HTML タグ作成する必要は無い。

document.addElement() があるので、canvas 要素を作って body に組み込んでしまえばよいのでした。

また、いくつか調べて分かった知識が追加されています。
・最近の (ES6) クラスの定義方法。
・「hidden された canvas をもう一つ用意し、ダブルバッファリングすると速い。」
等々。

/////////////////////
//
// Canvas 操作クラス
//
class CanvasOp {

    constructor(base_id, width, height) {

        this.width = width;
        this.height = height;

        // 描画用 Canvas およびダブルバッファリング用 Canvas を作成
        this.canvas = document.createElement("canvas");
        this.canvas_buff = document.createElement("canvas");
        this.canvas_buff.hidden = true;

        // サイズ設定
        this.canvas.width = width;
        this.canvas.height = height;
        this.canvas_buff.width = width;
        this.canvas_buff.height = height;

        // canvas を親要素に紐づけ
        if (base_id == null) {
            // 未指定なら document.body に紐づける
            document.body.appendChild(this.canvas);
            document.body.appendChild(this.canvas_buff);
        }
        else {
            const base = document.getElementById(base_id);
            base.appendChild(this.canvas);
            base.appendChild(this.canvas_buff);        
        }

        // 操作用 Context
        if (this.canvas.getContext && this.canvas_buff.getContext) {
            this.context = this.canvas.getContext("2d");
            this.context_buff = this.canvas_buff.getContext("2d");
        }
        else {
            this.context = null;
            this.context_buff = null;
            console.error("Error: Canvas not supported.");
        }

        // アニメーション用カウント
        this.count = 0;
    }

    ( 以下略 )
}

更新処理

更新処理、いくつか方法があるらしいですが window.requestAnimationFrame() を採用。
何がベストなのか、詳しくは調べてないけど使えればよいか。

コンストラクタで書いた通り、
バッファに書き込んで、それを表示用に持ってくる形で描画。

class CanvasOp {

    ( 前略 )

    // 更新処理
    update(ts)
    {
        // 空なら停止
        if (this.context == null) {
            return;
        }

        //////
        /// buff に描画
        ///
        // Clear
        this.context_buff.clearRect(0, 0, this.width, this.height);

        // 外枠
        this.context_buff.strokeRect(0, 0, this.width, this.height);

        // animathon
        this.count += 1;
        this.context_buff.beginPath();
        this.context_buff.fillRect(this.count - 5, this.count - 5, 10, 10);
        this.context_buff.stroke();
        if (this.count > 500) this.count = 0;

        //////
        /// Canvas に転送
        ///
        const data = this.context_buff.getImageData(0, 0, this.width, this.height);
        this.context.putImageData(data, 0, 0);

        // Update
        window.requestAnimationFrame((ts) => this.update(ts));
    };
}


メイン

HTML としては、Canvas を紐づける div だけ必要。

<div id="canvas_base"></div>

<script>
()
</script>


後は実行だけ。load 終わったら実行。
canvas.update() が実行されると、あとは続けて無限に実行。

// Canvas 紐づけ要素 ID
const BASE_ID = "canvas_base";

// 描画エリアサイズ
const WIDTH = 500;
const HEIGHT = 500;

////////////////////
//
// onload
//
window.onload = function()
{
    let canvas = new CanvasOp(BASE_ID, WIDTH, HEIGHT);
    canvas.update(0);
}


とりあえず下地ができたので、色々作ってみようと思います。

コード全文

<div id="canvas_base"></div>

<script>

// Canvas 紐づけ要素 ID
const BASE_ID = "canvas_base";

// 描画エリアサイズ
const WIDTH = 500;
const HEIGHT = 500;

/////////////////////
//
// Canvas 操作クラス
//
class CanvasOp {

    constructor(base_id, width, height) {

        this.width = width;
        this.height = height;

        // 描画用 Canvas およびダブルバッファリング用 Canvas を作成
        this.canvas = document.createElement("canvas");
        this.canvas_buff = document.createElement("canvas");
        // this.canvas = document.getElementById("canvas");
        // this.canvas_buff = document.getElementById("canvas_buff");
        this.canvas_buff.hidden = true;

        // サイズ設定
        this.canvas.width = width;
        this.canvas.height = height;
        this.canvas_buff.width = width;
        this.canvas_buff.height = height;

        // canvas を親要素に紐づけ
        if (base_id == null) {
            // 未指定なら document.body に紐づける
            document.body.appendChild(this.canvas);
            document.body.appendChild(this.canvas_buff);
        }
        else {
            const base = document.getElementById(base_id);
            base.appendChild(this.canvas);
            base.appendChild(this.canvas_buff);        
        }

        // 操作用 Context
        if (this.canvas.getContext && this.canvas_buff.getContext) {
            this.context = this.canvas.getContext("2d");
            this.context_buff = this.canvas_buff.getContext("2d");
        }
        else {
            this.context = null;
            this.context_buff = null;
            console.error("Error: Canvas not supported.");
        }

        // アニメーション用カウント
        this.count = 0;
    }

    // 更新処理
    update(ts)
    {
        // 空なら停止
        if (this.context == null) {
            return;
        }

        //////
        /// buff に描画
        ///
        // Clear
        this.context_buff.clearRect(0, 0, this.width, this.height);

        // 外枠
        this.context_buff.strokeRect(0, 0, this.width, this.height);

        // animation
        this.count += 1;
        this.context_buff.beginPath();
        this.context_buff.fillRect(this.count - 5, this.count - 5, 10, 10);
        this.context_buff.stroke();
        if (this.count > 500) this.count = 0;

        //////
        /// Canvas に転送
        ///
        const data = this.context_buff.getImageData(0, 0, this.width, this.height);
        this.context.putImageData(data, 0, 0);

        // Update
        window.requestAnimationFrame((ts) => this.update(ts));
    };
}


////////////////////
//
// onload
//
window.onload = function()
{
    let canvas = new CanvasOp(BASE_ID, WIDTH, HEIGHT);
    canvas.update(0);
}

</script>