指導員の自己紹介
2006 年に Firefox v2.0 がリリースされた際に、<canvas>
という言葉が頭に浮かびました。変換行列について説明している Ajaxian の記事を参考にして、<canvas>
ウェブアプリを初めて作成することにしました。それは Colorsphere(2007 年)です。色やグラフィック プリミティブの世界に私を没入させ、スケッチパッド(2007 ~ 2008 年)の作成に刺激を受けました。これは、「Paint より優れている」アプリケーションをブラウザで作成する取り組みです。
これらの実験は最終的に、私の長年の友人 Charles Pritchard とともにスタートアップ Mugtug を設立しました。Google では、HTML5 <canvas>
で Darkroom を開発しています。Darkroom は、ピクセルベースのフィルタの機能と、ベクターベースのタイポグラフィおよび描画を組み合わせた、非破壊的な写真共有アプリです。
はじめに
<canvas>
を使用すると、JavaScript プログラマーが画面上の色、ベクター、ピクセル(モニターの視覚的な構成)を完全に制御できます。
次の例では、テキスト効果を作成する <canvas>
の 1 つの領域を扱っています。<canvas>
で作成できるさまざまなテキスト効果は、想像できるほど多岐にわたります。これらのデモでは、可能な操作のサブセクションを取り上げています。この記事では「テキスト」を扱いますが、この方法はあらゆるベクター オブジェクトに適用できます。これにより、ゲームや他のアプリで魅力的なビジュアルを作成できます。
<canvas>
のテキスト シャドウ。<canvas>
での CSS に似たテキスト効果。クリッピング マスクの作成、<canvas>
での指標の検索、シャドウ プロパティの使用。- ネオン虹、ゼブラ柄の反射 - チェーン エフェクト
- Photoshop のようなテキスト効果。globalCompositeOperation、createLinearGradient、createPattern を使用した例です。
<canvas>
の内側と外側のシャドウ- ほとんど知られていない機能を明かす。時計回りと反時計回りの回転で反転したドロップ シャドウ(内側のシャドウ)を作成します。
- 残像 - 生成効果。
<canvas>
の生成ベースのテキスト効果。hsl() の色切り替えとwindow.requestAnimationFrame
を使用して動いている感じを作成します。
<canvas>
Canvas でのテキスト シャドウ
CSS3 仕様で私が気に入っている機能の一つに、(border-radius、web-グラデーションなどとともに)シャドウを作成できる機能があります。CSS と <canvas>
のシャドウの違いを認識することが重要です。具体的には次のとおりです。
CSS では 2 つのメソッドを使用します。div や span などのボックス要素には box-shadow を使用し、テキスト コンテンツには text-shadow を使用します。
<canvas>
には 1 種類のシャドウがあり、すべてのベクター オブジェクト(resourcemanager.moveTo、resourcemanager.lineTo、resourcemanager.bezierCurveTo、resourcemanager.quadradicCurveTo、resourcemanager.arc、resourcemanager.rect、resourcemanager.fillText、resourcemanager. インターフェースをテキストなど)に使用されます。<canvas>
にシャドウを作成するには、次の 4 つのプロパティをタップします。
- ctx.shadowColor = "red" // 文字列
- シャドウの色。RGB、RGBA、HSL、HEX などの入力は有効です。
- ctx.shadowOffsetX = 0; // 整数
- テキストに対するシャドウの水平方向の距離。
- ctx.shadowOffsetY = 0; // 整数
- テキストに対するシャドウの垂直方向の距離。
- ctx.shadowBlur = 10; // 整数
- シャドウのぼかし効果。値が大きいほど、ぼかしが強くなります。
まず、<canvas>
で CSS 効果をエミュレートする方法を見てみましょう。Google 画像で「css text-shadow」を検索すると、Line25、Stereoscopic、Shadow 3D のエミュレートに役立つデモがいくつか見つかりました。
立体 3D 効果(詳しくはアナグリフ画像を参照)は、シンプルなコードの一例であり、非常に活用されています。CSS の次の行を使用すると、3D の赤/シアンの眼鏡(3D 映画で見られるような眼鏡)を使用した場合に奥行きのある錯覚を作成できます。
text-shadow: -0.06em 0 0 red, 0.06em 0 0 cyan;
この文字列を <canvas>
に変換する際は、次の 2 つの点に注意してください。
- シャドウぼかし(3 つ目の値)はないため、実際にシャドウを実行する理由はありません。fillText でも同じ結果が得られます。
var text = "Hello world!"
ctx.fillStyle = "#000"
ctx.fillText(text, -7, 0);
ctx.fillStyle = "red"
ctx.fillText(text, 0, 0);
ctx.fillStyle = "cyan"
ctx.fillText(text, 7, 0);</pre>
- EM は
<canvas>
ではサポートされていないため、PX に変換する必要があります。PT、PC、EM、EX、PX などの変換率を求めるには、DOM で同じフォント プロパティの要素を作成し、幅を測定対象の形式に設定します。たとえば、EM から PX への変換を取得するには、DOM 要素を「height: 1em」で測定します。各 PX のオフセットは 1em です。
var font = "20px sans-serif"
var d = document.createElement("span");
d.style.cssText = "font: " + font + " height: 1em; display: block"
// the value to multiply PX 's by to convert to EM 's
var EM2PX = 1 / d.offsetHeight;</pre>
アルファ乗算の防止
Line25 のネオン効果などのより複雑な例では、shadowBlur プロパティを使用して効果を適切にエミュレートする必要があります。NEON 効果は複数のシャドウに依存するため、問題が発生します。<canvas>
では、各ベクター オブジェクトに設定できるシャドウは 1 つだけです。そのため、複数のシャドウを描画するには、その上に複数のバージョンのテキストを描画する必要があります。その結果、アルファ乗算が発生し、最終的にエッジがギザギザになります。
シャドウを表示中に ctx.fillStyle = "rgba(0,0,0,0)"
または "transparent"
を実行してテキストを非表示にしようとしましたが、これは無駄でした。シャドウは fillStyle アルファを掛けたものであるため、シャドウが fillStyle よりも不透明になることは決してありません。
幸いなことに、これを回避する方法があります。テキストからシャドウ オフセットを描画し、それらを分離して(重ならないように)、画面の端からテキストを非表示にできます。
var text = "Hello world!"
var blur = 10;
var width = ctx.measureText(text).width + blur * 2;
ctx.textBaseline = "top"
ctx.shadowColor = "#000"
ctx.shadowOffsetX = width;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = blur;
ctx.fillText(text, -width, 0);
テキスト ブロックをクリップする
これを少しクリーンアップするために、クリッピングパスを追加することで、(影の描画を許可しながら)fillText が最初に描画されないようにすることができます。テキストを囲むクリッピング パスを作成するには、テキストの高さ(従来、印刷機の「M」の文字の高さは「em-height」と呼ばれていました)とテキストの幅を把握する必要があります。幅は ctx.measureText().width
を使用して取得できますが、ctx.measureText().height
は存在しません。
幸いなことに、CSS ハックドリー(CSS 測定を使用して <canvas>
の古い実装を修正するその他の方法については、タイポグラフィ指標を参照)を使用して、同じフォント プロパティを使用して <span>
の offsetHeight
を測定することで、テキストの高さを見つけることができます。
var d = document.createElement("span");
d.font = "20px arial"
d.textContent = "Hello world!"
var emHeight = d.offsetHeight;
そこから、クリッピング パスとして使用する長方形を作成し、「シャドウ」を囲み、ダミーシェイプを削除します。
ctx.rect(0, 0, width, emHeight);
ctx.clip();
すべてを結び付けて最適化を進めていくと、シャドウにぼかしがなければ、fillText を使用して同じ効果を得ることができるので、クリッピング マスクを設定する手間を省くことができます。
var width = ctx.measureText(text).width;
var style = shadowStyles[text];
// add a background to the current effect
ctx.fillStyle = style.background;
ctx.fillRect(0, offsetY, ctx.canvas.width, textHeight - 1)
// parse text-shadows from css
var shadows = parseShadow(style.shadow);
// loop through the shadow collection
var n = shadows.length; while(n--) {
var shadow = shadows[n];
var totalWidth = width + shadow.blur * 2;
ctx.save();
ctx.beginPath();
ctx.rect(offsetX - shadow.blur, offsetY, offsetX + totalWidth, textHeight);
ctx.clip();
if (shadow.blur) { // just run shadow (clip text)
ctx.shadowColor = shadow.color;
ctx.shadowOffsetX = shadow.x + totalWidth;
ctx.shadowOffsetY = shadow.y;
ctx.shadowBlur = shadow.blur;
ctx.fillText(text, -totalWidth + offsetX, offsetY + metrics.top);
} else { // just run pseudo-shadow
ctx.fillStyle = shadow.color;
ctx.fillText(text, offsetX + (shadow.x||0), offsetY - (shadow.y||0) + metrics.top);
}
ctx.restore();
}
// drawing the text in the foreground
if (style.color) {
ctx.fillStyle = style.color;
ctx.fillText(text, offsetX, offsetY + metrics.top);
}
// jump to next em-line
ctx.translate(0, textHeight);
これらすべての <canvas>
コマンドを手動で入力するわけではないため、デモソースにシンプルなテキスト シャドウ パーサーを含めました。この方法で、CSS コマンドにフィードして <canvas>
コマンドを生成できます。これで、<canvas>
要素にさまざまなスタイルを関連付けることができます。これらと同じシャドウ効果を、WebFonts から SVG からインポートした複雑な図形、生成ベクター図形など、あらゆるベクター オブジェクトに使用できます。
中断(ピクセル プッシュとの接線)
このセクションを書いたとき、立体画像の例に興味が湧いてきました。<canvas>
と、少し異なる視点から撮影した 2 つの画像を使用して 3D ムービーの画面効果を作成するのはどのくらい難しいですか。それほど難しくはありません次のカーネルは、1 つ目の画像(data)の赤色チャンネルと 2 つ目の画像(data2)のシアン チャンネルを結合します。
data[i] = data[i] * 255 / 0xFF;
data[i+1] = 255 * data2[i+1] / 0xFF;
data[i+2] = 255 * data2[i+2] / 0xFF;
今では、2 台の iPhone を額にダクトテープで留めて、同時に「ビデオを録画」をクリックするだけで、HTML5 で独自の 3D ムービーを作成できます。ボランティアはいる?
虹色のネオン、ゼブラの反射 - チェーン効果
<canvas>
で複数のエフェクトを連結するのは簡単ですが、globalCompositeOperation(GCO)の基本的な知識が必要です。GIMP(または Photoshop)との処理を比較してみましょう。<canvas>
ダークには 12 個の GCO があり、lighter はレイヤ ブレンドモードと考えることができます。他の 10 個のオペレーションはアルファマスクとしてレイヤに適用されます(1 つのレイヤが他のレイヤのピクセルを削除します)。globalCompositeOperation は「レイヤ」(この場合はコード文字列)をつなぎ合わせ、新しい画期的な方法でそれらを組み合わせます。
globalCompositeOperation グラフには、実際の GCO モードが示されています。このグラフでは、想定される動作を詳細に確認するために、色スペクトルの大部分と複数レベルのアルファ透明度を使用しています。テキストの説明については、Mozilla の globalCompositeOperation リファレンスを確認することをおすすめします。このオペレーションの仕組みについては、Porter Duff の Compositing Digital Images をご覧ください。
私の好きなモードは globalCompositeOperation="lighter" です。Lighter は、光が混ざるのと同じように、追加されたピクセルを合成します。赤、緑、白の光が最大強度になると、白色光が表示されます。これは、特に <canvas>
が低い globalAlpha に設定されている場合に、試してみると良い機能で、より細かい制御と滑らかなエッジが可能になります。Lighter はさまざまな用途に応用されています。私の最近のお気に入りは、http://weavesilk.com/ にある HTML5 のデスクトップ背景作成ツールです。私のデモの 1 つである Breathing Galaxies(JS1k)もライトモードを使用しています。この 2 つの例からパターンを描画して、このモードがもたらす効果を確認します。
globalCompositeOperation ブラウザ処理。
ネオン虹ジッター効果
次のデモでは、globalCompositeOperation を使用して効果を連結することで、輪郭がジッターでPhotoshop のようなネオン レインボー グローを実現します(ソースイン、明色、暗色)。このデモは「<canvas>
のテキスト シャドウ」デモを進行したもので、同じ戦略を使用してシャドウをテキストから分離しています(前のセクションを参照)。
function neonLightEffect() {
var text = "alert('"+String.fromCharCode(0x2665)+"')";
var font = "120px Futura, Helvetica, sans-serif";
var jitter = 25; // the distance of the maximum jitter
var offsetX = 30;
var offsetY = 70;
var blur = getBlurValue(100);
// save state
ctx.save();
ctx.font = font;
// calculate width + height of text-block
var metrics = getMetrics(text, font);
// create clipping mask around text-effect
ctx.rect(offsetX - blur/2, offsetY - blur/2,
offsetX + metrics.width + blur, metrics.height + blur);
ctx.clip();
// create shadow-blur to mask rainbow onto (since shadowColor doesn't accept gradients)
ctx.save();
ctx.fillStyle = "#fff";
ctx.shadowColor = "rgba(0,0,0,1)";
ctx.shadowOffsetX = metrics.width + blur;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = blur;
ctx.fillText(text, -metrics.width + offsetX - blur, offsetY + metrics.top);
ctx.restore();
// create the rainbow linear-gradient
var gradient = ctx.createLinearGradient(0, 0, metrics.width, 0);
gradient.addColorStop(0, "rgba(255, 0, 0, 1)");
gradient.addColorStop(0.15, "rgba(255, 255, 0, 1)");
gradient.addColorStop(0.3, "rgba(0, 255, 0, 1)");
gradient.addColorStop(0.5, "rgba(0, 255, 255, 1)");
gradient.addColorStop(0.65, "rgba(0, 0, 255, 1)");
gradient.addColorStop(0.8, "rgba(255, 0, 255, 1)");
gradient.addColorStop(1, "rgba(255, 0, 0, 1)");
// change composite so source is applied within the shadow-blur
ctx.globalCompositeOperation = "source-atop";
// apply gradient to shadow-blur
ctx.fillStyle = gradient;
ctx.fillRect(offsetX - jitter/2, offsetY,
metrics.width + offsetX, metrics.height + offsetY);
// change composite to mix as light
ctx.globalCompositeOperation = "lighter";
// multiply the layer
ctx.globalAlpha = 0.7
ctx.drawImage(ctx.canvas, 0, 0);
ctx.drawImage(ctx.canvas, 0, 0);
ctx.globalAlpha = 1
// draw white-text ontop of glow
ctx.fillStyle = "rgba(255,255,255,0.95)";
ctx.fillText(text, offsetX, offsetY + metrics.top);
// created jittered stroke
ctx.lineWidth = 0.80;
ctx.strokeStyle = "rgba(255,255,255,0.25)";
var i = 10; while(i--) {
var left = jitter / 2 - Math.random() * jitter;
var top = jitter / 2 - Math.random() * jitter;
ctx.strokeText(text, left + offsetX, top + offsetY + metrics.top);
}
ctx.strokeStyle = "rgba(0,0,0,0.20)";
ctx.strokeText(text, offsetX, offsetY + metrics.top);
ctx.restore();
};
ゼブラの反射効果
ゼブラ リフレクション効果は、CSS でページにひと工夫加える方法に関する WebDesignerWall の優れたリソースを参考にしました。この方法ではアイデアをさらに進め、iTunes で見られるようなテキストの「反射」を作成します。この効果は、fillColor(白)、createPattern(zebra.png)、linearGradient(shine)を組み合わせたものです。各ベクター オブジェクトに複数の塗りつぶしタイプを適用できることを示しています。
function sleekZebraEffect() {
// inspired by - http://www.webdesignerwall.com/demo/css-gradient-text/
var text = "Sleek Zebra...";
var font = "100px Futura, Helvetica, sans-serif";
// save state
ctx.save();
ctx.font = font;
// getMetrics calculates:
// width + height of text-block
// top + middle + bottom baseline
var metrics = getMetrics(text, font);
var offsetRefectionY = -20;
var offsetY = 70;
var offsetX = 60;
// throwing a linear-gradient in to shine up the text
var gradient = ctx.createLinearGradient(0, offsetY, 0, metrics.height + offsetY);
gradient.addColorStop(0.1, '#000');
gradient.addColorStop(0.35, '#fff');
gradient.addColorStop(0.65, '#fff');
gradient.addColorStop(1.0, '#000');
ctx.fillStyle = gradient
ctx.fillText(text, offsetX, offsetY + metrics.top);
// draw reflected text
ctx.save();
ctx.globalCompositeOperation = "source-over";
ctx.translate(0, metrics.height + offsetRefectionY)
ctx.scale(1, -1);
ctx.font = font;
ctx.fillStyle = "#fff";
ctx.fillText(text, offsetX, -metrics.height - offsetY + metrics.top);
ctx.scale(1, -1);
// cut the gradient out of the reflected text
ctx.globalCompositeOperation = "destination-out";
var gradient = ctx.createLinearGradient(0, offsetY, 0, metrics.height + offsetY);
gradient.addColorStop(0.0, 'rgba(0,0,0,0.65)');
gradient.addColorStop(1.0, '#000');
ctx.fillStyle = gradient;
ctx.fillRect(offsetX, offsetY, metrics.width, metrics.height);
// restore back to original transform state
ctx.restore();
// using source-atop to allow the transparent .png to show through to the gradient
ctx.globalCompositeOperation = "source-atop";
// creating pattern from <image> sourced.
ctx.fillStyle = ctx.createPattern(image, 'repeat');
// fill the height of two em-boxes, to encompass both normal and reflected state
ctx.fillRect(offsetX, offsetY, metrics.width, metrics.height * 2);
ctx.restore();
};
Canvas の内側と外側のシャドウ
<canvas>
の仕様では、「内側」と「外側」のシャドウについては触れていません。実際、最初は「内側の」シャドウはサポートされていないと思われがちですが、これは誤りです。有効にするのは少し難しいかもしれません。F1LT3R の最近の投稿で提案されているように、時計回りと反時計回りのワインディング ルールという独自のプロパティを使用して、内側のシャドウを作成できます。これを行うには、コンテナの長方形を描画して「内側のシャドウ」を作成します。次に、反対の巻き線ルールを使用してカットアウト図形を描画し、その図形の逆を作成します。
次の例では、inner-shadow と fillStyle を色 + グラデーション + パターンで同時にスタイル設定できるようにしています。パターンの回転は個別に指定できます。ゼブラ ストライプが互いに直交するようになりました。境界ボックスのサイズのクリッピング マスクを使用して、カットアウト形状を囲む超大型コンテナが不要になり、シャドウの不要な部分が処理されなくて済むため、速度が向上します。
function innerShadow() {
function drawShape() { // draw anti-clockwise
ctx.arc(0, 0, 100, 0, Math.PI * 2, true); // Outer circle
ctx.moveTo(70, 0);
ctx.arc(0, 0, 70, 0, Math.PI, false); // Mouth
ctx.moveTo(-20, -20);
ctx.arc(30, -30, 10, 0, Math.PI * 2, false); // Left eye
ctx.moveTo(140, 70);
ctx.arc(-20, -30, 10, 0, Math.PI * 2, false); // Right eye
};
var width = 200;
var offset = width + 50;
var innerColor = "rgba(0,0,0,1)";
var outerColor = "rgba(0,0,0,1)";
ctx.translate(150, 170);
// apply inner-shadow
ctx.save();
ctx.fillStyle = "#000";
ctx.shadowColor = innerColor;
ctx.shadowBlur = getBlurValue(120);
ctx.shadowOffsetX = -15;
ctx.shadowOffsetY = 15;
// create clipping path (around blur + shape, preventing outer-rect blurring)
ctx.beginPath();
ctx.rect(-offset/2, -offset/2, offset, offset);
ctx.clip();
// apply inner-shadow (w/ clockwise vs. anti-clockwise cutout)
ctx.beginPath();
ctx.rect(-offset/2, -offset/2, offset, offset);
drawShape();
ctx.fill();
ctx.restore();
// cutout temporary rectangle used to create inner-shadow
ctx.globalCompositeOperation = "destination-out";
ctx.fill();
// prepare vector paths
ctx.beginPath();
drawShape();
// apply fill-gradient to inner-shadow
ctx.save();
ctx.globalCompositeOperation = "source-in";
var gradient = ctx.createLinearGradient(-offset/2, 0, offset/2, 0);
gradient.addColorStop(0.3, '#ff0');
gradient.addColorStop(0.7, '#f00');
ctx.fillStyle = gradient;
ctx.fill();
// apply fill-pattern to inner-shadow
ctx.globalCompositeOperation = "source-atop";
ctx.globalAlpha = 1;
ctx.rotate(0.9);
ctx.fillStyle = ctx.createPattern(image, 'repeat');
ctx.fill();
ctx.restore();
// apply fill-gradient
ctx.save();
ctx.globalCompositeOperation = "destination-over";
var gradient = ctx.createLinearGradient(-offset/2, -offset/2, offset/2, offset/2);
gradient.addColorStop(0.1, '#f00');
gradient.addColorStop(0.5, 'rgba(255,255,0,1)');
gradient.addColorStop(1.0, '#00f');
ctx.fillStyle = gradient
ctx.fill();
// apply fill-pattern
ctx.globalCompositeOperation = "source-atop";
ctx.globalAlpha = 0.2;
ctx.rotate(-0.4);
ctx.fillStyle = ctx.createPattern(image, 'repeat');
ctx.fill();
ctx.restore();
// apply outer-shadow (color-only without temporary layer)
ctx.globalCompositeOperation = "destination-over";
ctx.shadowColor = outerColor;
ctx.shadowBlur = 40;
ctx.shadowOffsetX = 15;
ctx.shadowOffsetY = 10;
ctx.fillStyle = "#fff";
ctx.fill();
};
これらの例からわかるように、globalCompositeOperation を使用すると、エフェクトを連結して、より複雑なエフェクトを生成できます(マスキングとブレンディングを利用)。画面はオシャレです ;)
残像 - 生成効果
<canvas>
で、Unicode 文字 0x2708 から次のように記述します。
網掛けのある例のようになります。
これを実現するには、細い lineWidth(0.25)で ctx.strokeText()
を複数回呼び出しながら、x のオフセットとアルファをゆっくりと小さくして、ベクター要素に動いている感じを与えます。
要素の XY 位置を正弦/余弦にマッピングし、HSL プロパティを使用して色を循環させることで、この「バイオハザード」の例のような、より興味深い効果を作成できます。
HSL: 色相、彩度、明度(1978 年)
HSL は、CSS3 仕様で新たにサポートされる形式です。HEX がコンピュータ用に設計されたのに対して、HSL は人が読めるように設計されています。
HSL のしやすさを説明しています。カラースペクトルを巡回するには、単純に「hue」を 360 度から増やします。色相は円柱状にスペクトルにマッピングされます。明度は、色の暗さ/明度を制御します。0% は黒のピクセルを示し、100% は白のピクセルを示します。彩度は、色の明るさと鮮明さを制御します。グレーは彩度 0% で作成され、鮮やかな色は 100% の値を使用して作成されます。
HSL は最近の標準であるため、古いブラウザを引き続きサポートすることもできますが、その場合は色空間変換を利用できます。次のコードは、HSL オブジェクト { H: 360, S: 100, L: 100} を受け取り、RGB オブジェクト { R: 255, G: 255, B: 255 } を出力します。そこから、これらの値を使用して rgb または rgba の文字列を作成できます。詳細については、HSL に関する Wikipedia の洞察に満ちた記事をご覧ください。
// HSL (1978) = H: Hue / S: Saturation / L: Lightness
HSL_RGB = function (o) { // { H: 0-360, S: 0-100, L: 0-100 }
var H = o.H / 360,
S = o.S / 100,
L = o.L / 100,
R, G, B, _1, _2;
function Hue_2_RGB(v1, v2, vH) {
if (vH < 0) vH += 1;
if (vH > 1) vH -= 1;
if ((6 * vH) < 1) return v1 + (v2 - v1) * 6 * vH;
if ((2 * vH) < 1) return v2;
if ((3 * vH) < 2) return v1 + (v2 - v1) * ((2 / 3) - vH) * 6;
return v1;
}
if (S == 0) { // HSL from 0 to 1
R = L * 255;
G = L * 255;
B = L * 255;
} else {
if (L < 0.5) {
_2 = L * (1 + S);
} else {
_2 = (L + S) - (S * L);
}
_1 = 2 * L - _2;
R = 255 * Hue_2_RGB(_1, _2, H + (1 / 3));
G = 255 * Hue_2_RGB(_1, _2, H);
B = 255 * Hue_2_RGB(_1, _2, H - (1 / 3));
}
return {
R: R,
G: G,
B: B
};
};
requestAnimationFrame によるアニメーションの作成
これまで、JavaScript でアニメーションを作成する場合、setTimeout
と setInterval
という 2 つの選択肢がありました。
window.requestAnimationFrame
は、この両方に代わる新しい規格です。使用可能なリソースに基づいてアニメーションをブラウザで制御できるようにすることで、世界の電力(とコンピュータの負荷)を節約できます。重要な機能には次のようなものがあります。
- ユーザーがフレームが存在する場合は、不要なリソースが使用されないように、アニメーションの速度が遅くなったり、完全に停止したりすることがあります。
- 60 FPS のフレームレートには上限があります。これは、人間が認識できるレベルをはるかに上回っているためです(ほとんどの人間は 30 FPS でアニメーションを「流動的」と見なします)。
現時点では、requestAnimationFrame
を使用するにはベンダー固有の接頭辞が必要です。Paul Irish は、スマート アニメーション用の requestAnimationFrame で、ベンダー間をサポートする shim レイヤを作成しました。
// shim layer with setTimeout fallback
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function */ callback, /* DOMElement */ element){
window.setTimeout(callback, 1000 / 60);
};
})();
もう少し詳しく説明すると、新しい標準に切り替えると同時に、古いブラウザをさらにサポートする requestAnimationFrame.js(対処すべき機能がいくつかあります)などのポリフィルと結び付けることもできます。
(function animate() {
var i = 50;
while(i--) {
if (n > endpos) return;
n += definition;
ctx.globalAlpha = (0.5 - (n + startpos) / endpos) * alpha;
if (doColorCycle) {
hue = n + color;
ctx.strokeStyle = "hsl(" + (hue % 360) + ",99%,50%)"; // iterate hue
}
var x = cos(n / cosdiv) * n * cosmult; // cosine
var y = sin(n / sindiv) * n * sinmult; // sin
ctx.strokeText(text, x + xoffset, y + yoffset); // draw rainbow text
}
timeout = window.requestAnimationFrame(animate, 0);
})();
ソースコード
ブラウザ ベンダー全体からのサポートにより、PhoneGap を使用して iPhone/Android/PC の実行可能ファイルに移植できる <canvas>
の今後について、疑問の余地はありません。