【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>