レンダリング速度向上のためにやったこと
uupaa.js / uuCanvas.js / uuAltCSS.js コードリード用のエントリです。
uuCanvas.js のコードを眺めてて、
// CanvasRenderingContext2D.prototype.fill function fill(wire, path) { var fg = ""; // fragment : : this._elm.insertAdjacentHTML("BeforeEnd", fg); }
とかやってることに気が付きました。
# insertAdjacentHTML("BeforeEnd", ...) は 文字列から要素を作成して appendChild する JScript 独自メソッド
fill() するたびに、DOMツリーにちょこちょこ要素を追加しているし、そのつど再描画が発生するので2重に遅くなるはず。
DOM アクセスや再描画は一つの実行単位内に纏めるべき
ctx.lock() と ctx.unlock() のようにスクリーンへの描画をロックするメソッドを追加し、どうなるか試してみました。
// こちらは prototype にメソッドを追加 uu.mix(uu.canvas.VML2D.prototype, { lock: lock, // ctx.lock(clearScreen = 0) unlock: unlock, // ctx.unlock() }); // こちらは ctx の初期化処理に追加 ctx._lockstock = []; // lock stock ctx._lockstate = 0; // lock state, 0: unlock, 1: lock, 2: lock + clear
// fill() で DOM に書き込まないようにちょっと変更 // CanvasRenderingContext2D.prototype.fill function fill(wire, path) { var fg = ""; // fragment : //this._elm.insertAdjacentHTML("BeforeEnd", fg); this._lockstate ? this._lockstock.push(fg) : this._elm.insertAdjacentHTML("BeforeEnd", fg); } // lock() と unlock() を実装 // CanvasRenderingContext2D.prototype.lock function lock(clearScreen) { // @param Boolean(= false): if (this._lockstate) { throw "duplicate lock"; } this._lockstate = clearScreen ? 2 : 1; } // CanvasRenderingContext2D.prototype.unlock function unlock() { if (this._lockstate) { (this._lockstate === 2) && _clear(this); if (this._lockstock.length) { this._elm.insertAdjacentHTML("BeforeEnd", this._lockstock.join("")); } } this._lockstock = []; this._lockstate = 0; }
使い方(How to use)
2D Canvas に書き込んでいる処理の前後に、ctx.lock(), ctx.unlock() を追加し、clearRect() を削ります。
// 変更前 function drawLoop() { ctx.clearRect(0, 0, canvasWidth, canvasHeight); scene.camera.x = 70*Math.sin(count); scene.camera.y = 70; scene.camera.z = 70*Math.cos(count); scene.cameraRotation = count / 10; count += 0.01; scene.draw(); // この中で描画 }
// 変更後 function drawLoop() { // ctx.clearRect(0, 0, canvasWidth, canvasHeight); ctx.lock(1); scene.camera.x = 70*Math.sin(count); scene.camera.y = 70; scene.camera.z = 70*Math.cos(count); scene.cameraRotation = count / 10; count += 0.01; scene.draw(); // この中で描画 ctx.unlock(); }
この修正により、excanvas.js と比べ、描画速度が120〜140%に向上しました(in IE6 VML MODE)。
説明
- ctx.lock() は、スクリーンへの書き込みをロックし、ctx.unlock() で纏めて描画します。
- ctx.lock(1) とすると ctx.unlock() が呼ばれたタイミングで、描画結果をクリアしてからスクリーンに書き込みます。
ctx.lock(), ctx.unlock() は uuCanvas.js がサポートするブラウザ(IE6+, Opera9.5+, Safari3.1+, Google Chrome2+, Firefox2+)で利用可能ですが、描画速度が向上するのは、IE だけです。
uuAltCSS.js も uuCanvas.js の機能を利用しています
uuAltCSS.js で、 -uu-box-shadow: blue 0px 0px 5px; 等とした場合の擬似的な影のレンダリングがとても重いため、uuAltCSS.js 専用の I/F を uuCanvas.js に追加しました。
// CanvasRenderingContext2D.prototype.qstroke - quick stroke function qstroke(hexcolor, alpha, lineWidth) { var fg = '<v:shape style="position:absolute;width:10px;height:10px' + '" filled="f" stroked="t" coordsize="100,100" path="' + this._path.join("") + '"><v:stroke color="' + hexcolor + '" opacity="' + alpha.toFixed(2) + '" weight="' + lineWidth + 'px" /></v:shape>'; this._lockstate ? this._lockstock.push(fg) : this._elm.insertAdjacentHTML("BeforeEnd", fg); } // CanvasRenderingContext2D.prototype.qstrokeRect function qstrokeRect(x, y, w, h, hexcolor, alpha, lineWidth) { var hm = _HALF_ZOOM, ix = x * _ZOOM, iy = y * _ZOOM, iw = (x + w) * _ZOOM, ih = (y + h) * _ZOOM; this._path = ["m " + (ix - hm) + " " + (iy - hm) + "l " + (ix - hm) + " " + (ih - hm) + "l " + (iw - hm) + " " + (ih - hm) + "l " + (iw - hm) + " " + (iy - hm) + "l " + (ix - hm) + " " + (iy - hm) + "x"]; this.qstroke(hexcolor, alpha, lineWidth); }
uuAltCSS.js 側のコードにも最適化を加えました。
function drawFakeShadowIE(ctx, x, y, width, height, rgba, blur, radius) { var i = 0, j = 0, k, step = 1, line = 5, r = radius, hexcolor = uu.color.hex(rgba); if (uu.ie6 && uu.light) { step *= 3, line *= 2.5; } ctx.lock(); if (r[0] === 0 && (r[0] === r[1] && r[0] === r[2] && r[0] === r[3])) { for (; i < blur; i += step) { k = i / blur; ctx.qstrokeRect(x + i, y + i, width - (i * 2), height - (i * 2), hexcolor, k * k * k, line); } } else { for (; i < blur; i += step) { k = i / blur; j += 0.5; boxpath(ctx, x + i, y + i, width - (i * 2), height - (i * 2), [r[0] - j, r[1] - j, r[2] - j, r[3] - j]); ctx.qstroke(hexcolor, k * k * k, line); } } ctx.unlock(); }
これらの最適化を加えたことにより、ページ読み込み時のレンダリングのもたつきが見えなくなり、画面リサイズ時にもキビキビと動作するようになりました。
これらの変更は
uupaa.js version 0.7 用のリポジトリに対して行っています。
spin-off プロジェクトの uuCanvas.js や uuAltCSS.js には、まだ反映していません。