CSS の背景画像を Three.js で描画する方法
先日、 HTML5Rocks で「Canvas-driven background images」という記事が公開され、 canvas や WebGL で描画した画像を CSS の背景画像として適用できる -webkit-canvas の使い方が解説されていました。
canvas や WebGL はアニメーション GIF などに比べて圧倒的に滑らかなアニメーションが可能ですし、 GPU アクセラレーションが効くので処理負荷も(滑らかさのわりには)抑えられます。また、簡単な図形であれば JS で描画したほうがデータ量を削減できることが多いはずです。
そこで、本日はその -webkit-canvas の使い方と、加えて WebGL のラッパーライブラリである Three.js と組み合わせる方法をご紹介します。 HTML 上での動的画像の取り回しを大きく改善する画期的な技術だと思いますので、参考にしていただければ幸いです。
-webkit-canvas 属性
CSS の背景画像を canvas で描画するには、 WebKit の独自拡張である -webkit-canvas を使います。扱いは linear-gradient などとほぼ同じで、 background 属性(または background-image 属性)に画像の URL の代わりに記述します。括弧の中は JavaScript 側でコンテキストを識別するための文字列です。
.canvas-bg { background: -webkit-canvas(canvas-bg); }
JavaScript 側では document.getCSSCanvasContext() メソッドを使用して canvas の描画コンテキストを取得します。第一引数は canvas 要素の getContext() メソッドと同じ、第二引数は -webkit-canvas の括弧内に指定した文字列、以降は canvas の幅と高さです。この描画コンテキストに通常どおり描画していけば、その結果が背景画像として表示されます。 requestAnimationFrame() を使用したアニメーションも可能です。
var ctx = document.getCSSCanvasContext('2d', 'canvas-bg', 100, 100); function tick() { ctx.fillStyle = 'black'; ctx.globalCompositeOperation = 'source-over'; ctx.fillRect(0, 0, 100, 100); ctx.globalCompositeOperation = 'lighter'; var colors = ['#f00', '#0f0', '#00f']; var angle = Math.PI * ((+new Date()) % 2000 / 1000) for(var i = 0 ; i < 3 ; i++) { var a = angle + Math.PI * 2 / 3 * i; ctx.fillStyle = colors[i]; ctx.beginPath(); ctx.arc(Math.sin(a) * 25 + 50, Math.cos(a) * 25 + 50, 30, 0, 2*Math.PI, true); ctx.fill(); } webkitRequestAnimationFrame(tick); } tick();
以下はこのスタイルと JavaScript を実際に適用したものです。 -webkit-canvas に対応したブラウザ(Chrome 25 以上、もしくは Mac / iOS の Safari 6 以上)であれば、赤、青、緑の 3 つの円がくるくると回転するアニメーションが見れるはずです。また、静止画の背景画像と同様に画像がリピートするのも確認できます。
これだけだとなにが嬉しいのかわかりませんが、具体的なメリットについては記事の最後にまとめようと思います。
Three.js で背景画像を描画する
canvas が使えるだけでもじゅうぶん嬉しいのですが、さらに WebGL を使って背景画像を描画することも可能です。単に document.getCSSCanvasContext() の第一引数に "experimental-webgl" を指定するだけで、 WebGL 用の描画コンテキストが取得できます。
var gl = document.getCSSCanvasContext('experimental-webgl', 'canvas-bg', 100, 100);
そして、 WebGL とくれば Three.js ですよね。 Three.js の WebGLRenderer はコンストラクタ引数で canvas 要素を指定できるのですが、残念ながら描画コンテキストを直接渡すことはできません。そこで、 canvas 要素のように振る舞うダミーオブジェクトを適当に作って、それを渡してやります。
var fakeCanvas = { getContext: function() { return document.getCSSCanvasContext( 'experimental-webgl', 'webgl-bg', this.width, this.height); }, width: 200, height: 200 }; var renderer = new THREE.WebGLRenderer({ canvas:fakeCanvas });
あとは、生成した WebGLRenderer で通常どおり描画してやれば、それが背景画像として表示されます。以下はこの方法で背景画像として回転する地球を表示した例です。 Mac 版 Safari では開発メニューで WebGL を有効にしてください。 iOS では表示できません。
ソースはこんな感じです。処理内容は @IT さんに掲載していただいた Three.js 入門記事のものとほぼ同じなので、詳細はそちらを参照してください。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <style> body { margin:0; padding:0; } .webgl-bg { background: -webkit-canvas(webgl-bg); } </style> <script src="three.min.js"></script> </head> <body> <div class="webgl-bg" style="width:200px; height:200px;"></div> <script> // canvas要素として振る舞うダミーオブジェクトを作成 var fakeCanvas = { getContext: function() { return document.getCSSCanvasContext( 'experimental-webgl', 'webgl-bg', 200, 200); }, width: 200, height: 200 }; // レンダラ・シーンの初期化 var scene = new THREE.Scene(); var renderer = new THREE.WebGLRenderer({ canvas:fakeCanvas }); renderer.setClearColorHex(0x000000, 1); // カメラの作成 var camera = new THREE.PerspectiveCamera(15, 200 / 200); camera.position = new THREE.Vector3(0, 0, 8); camera.lookAt(new THREE.Vector3(0, 0, 0)); scene.add(camera); // ライトの作成 var light = new THREE.DirectionalLight(0xcccccc); light.position = new THREE.Vector3(0.577, 0.577, 0.577); var ambient = new THREE.AmbientLight(0x333333); scene.add(light); scene.add(ambient); // 表示する物体の作成 var geometry = new THREE.SphereGeometry(1, 32, 16); var texture = THREE.ImageUtils.loadTexture('earth.jpg') var material = new THREE.MeshPhongMaterial({ color: 0xffffff, specular: 0xcccccc, shininess:50, ambient: 0xffffff, map: texture, bumpMap:texture, bumpScale: 0.05 }); var mesh = new THREE.Mesh(geometry, material); scene.add(mesh); // レンダリング var baseTime = +new Date; function render() { requestAnimationFrame(render); mesh.rotation.y = 0.3 * (+new Date - baseTime) / 1000; renderer.render(scene, camera); }; render(); </script> </body> </html>
-webkit-canvas のメリット
canvas タグを使わずに -webkit-canvas を使って canvas や WebGL の画像を表示するメリットは、基本的には img タグに対する CSS の背景画像指定と同じです。 HTML の構造を変えずに適用できるので、ページデザインの一要素としての動的画像の導入がとてもやりやすくなります。例えばアニメーション GIF で表示しているロード中インジケータを -webkit-canvas に差し替えるのはとても簡単です。以下のように記述すれば、 -webkit-canvas をサポートしているブラウザでは canvas / WebGL による滑らかなアニメーションを表示し、そうでなければ GIF を表示する、というフォールバックも実現できます。
.loading-indicator { background: url(loding.gif); background: -webkit-canvas(loading); }
さらに、 canvas タグでは困難だが -webkit-canvas なら簡単に実現できる、ということもいくつかあります。上の 2d canvas のサンプルの所で示した画像のリピートなんかがその一例ですね。生成画像をページ内の複数の箇所で表示したい場合も、 canvas タグでは明示的に画像をコピーする必要がありましたが、 -webkit-canvas なら同じ CSS クラスを指定するだけです。
また、 -webkit-canvas は linear-gradient などと同じ CSS image value のひとつなので(たぶん)、 background 以外にも list-style や border-image にも適用できます。以下は border-image の例。
この機能が普及すれば、 canvas や WebGL の応用範囲が大きく広がるのは間違いないでしょう。 WebKit 以外のブラウザもぜひ追従してほしいところです。
詳しくはこちらの記事をどうぞ!
この記事にコメントする