Canvas tutorial:Applying styles and colors
出典: MDC
目次 |
図形を描く についての章では私はデフォルトの線と塗りつぶしのスタイルしか使いませんでした。この章では使用できる全ての canvas オプションを探検していき、最終的に私たちの図形をもう少し魅力的なものにしていきます。
[編集] 色
今までは描画コンテクストのメソッドを見るだけでした。色を図形に適用したい場合使用することができる2つの重要なプロパティがあります: fillStyle と strokeStyle。
fillStyle = color
strokeStyle = color
strokeStyle は図形の輪郭の色を、fillStyle は塗りつぶしの色を設定するのに使用します。color は CSS 色の値か、グラデーションオブジェクト、パターンオブジェクトをあらわす文字列が可能です。グラデーションとパターンオブジェクトは後で見ることにします。標準では、ストロークと塗りつぶしの色は黒(CSS 色の値 #000000)に設定されています。
入力すべき正しい値は、仕様によると、CSS3 color values です。下の例のそれぞれは同じ色を記述しています。
// これれは全て fillStyle を 'orange' に設定しています。 ctx.fillStyle = "orange"; ctx.fillStyle = "#FFA500"; ctx.fillStyle = "rgb(255,165,0)"; ctx.fillStyle = "rgba(255,165,0,1)";
Note: 現在のところ、Gecko エンジンに全ての CSS 3 色の値がサポートされているわけではありません。例えば、色の値 hsl(100%,25%,0) や rgb(0,100%,0) は許可されていません。もし上記のものを我慢すれば、問題に出会わずにすむでしょう。
Note: もし strokeStyle と/または fillStyle プロパティを設定すると、新しい値はその後描かれるすべての図形の標準になります。異なる色を望むすべての図形に対して、再び fillStyle か strokeStyle プロパティを割り当てる必要があります。
[編集] fillStyle の例
この例では、再び for ループをそれぞれ異なった色の矩形のグリッドを描くために使っています。ここでは目を瞠ることは何もありません。結果の画像は右の画像のようになるはずです。私は2つの変数 i と j をそれぞれの正方形のためにユニークな RGB カラーを作るためにつかっています。私は赤と緑の値のみをいじっています。青のチャンネルは固定された値です。チャンネルをいじることによって、あなたはすべての種類のパレットを作ることができます。ステップを増やすことで、あなたは Photoshop が使用しているカラーパレットに似たものを実現することができます。
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
for (i=0;i<6;i++){
for (j=0;j<6;j++){
ctx.fillStyle = 'rgb(' + Math.floor(255-42.5*i) + ',' +
Math.floor(255-42.5*j) + ',0)';
ctx.fillRect(j*25,i*25,25,25);
}
}
}
[編集] strokeStyle の例
この例は上の例に似ていますが、今回は strokeStyle をつかっています。ここでは正方形ではなく円を描くために arc メソッドを使っています。
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
for (i=0;i<6;i++){
for (j=0;j<6;j++){
ctx.strokeStyle = 'rgb(0,' + Math.floor(255-42.5*i) + ',' +
Math.floor(255-42.5*j) + ')';
ctx.beginPath();
ctx.arc(12.5+j*25,12.5+i*25,10,0,Math.PI*2,true);
ctx.stroke();
}
}
}
[編集] 透明度
不透明な図形を canvas に描くこと以外に、私たちは半透明な図形を描くことができます。 globalAlpha プロパティを設定するか、半透明な色を輪郭や塗りつぶし、またはその両方のスタイルに割り当てることでこれを行うことができます。
globalAlpha = transparency value
このプロパティは値の正しい範囲は 0.0 (完全な透明) から 1.0 (完全な不透明) です。標準では、このプロパティは 1.0 (完全な不透明) に設定されています。
canvas に似たような透明度をもつ沢山の図形を描きたいと思うなら、globalAlpha プロパティが便利でしょう。しかし、私は次の選択肢のほうがもう少し実用的だと思います。
strokeStyle と fillStyle プロパティは CSS 3 の色の値を受け入れるので、透明色を割り当てるには以下の記法を使用することができます。
// 透明色を輪郭と塗りつぶしスタイルに割り当てる ctx.strokeStyle = "rgba(255,0,0,0.5)"; ctx.fillStyle = "rgba(255,0,0,0.5)";
rgba() 関数は rgb() 関数に似ていますが、1つ余分なパラメタをもっています。最後のパラメタはこの実際の色の透明度を設定します。値の正しい範囲は再び 0.0 (完全な透明) から 1.0 (完全な不透明) の間です。
[編集] globalAlpha の例
この例では四つの異なる色の正方形で背景を描いています。それらの上に、一連の半透明の円を描いています。globalAlphaプロパティは 0.2 に設定されると、その時点からすべての図形に使われます。for ループのすべてのステップで半径を増やしながら円が描かれます。最終的な結果は円形グラデーションになります。互いの上により多くの円をかぶせることによって結果として既に描かれた円の透明度を減少させます。 ステップカウントを増加させてより多くの円を描くことによって背景は画像の中心から完全に見えなくなるでしょう。
- この例は Firefox 1.5 beta1 では動きません。これを実際に見るには nightly ブランチビルドかより新しいリリースをまつ必要があります。
- この例は Safari で壊れており、色が正しく指定されていないかのようになります。例の色は '#09F)' として指定されており仕様によると不正です。しかし、Firefox は不正な色の定義を受け入れます。
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
// 背景を描く
ctx.fillStyle = '#FD0';
ctx.fillRect(0,0,75,75);
ctx.fillStyle = '#6C0';
ctx.fillRect(75,0,75,75);
ctx.fillStyle = '#09F';
ctx.fillRect(0,75,75,75);
ctx.fillStyle = '#F30';
ctx.fillRect(75,75,150,150);
ctx.fillStyle = '#FFF';
// 透明度の値を設定する
ctx.globalAlpha = 0.2;
// 半透明な円を描く
for (i=0;i<7;i++){
ctx.beginPath();
ctx.arc(75,75,10+10*i,0,Math.PI*2,true);
ctx.fill();
}
}
[編集] rgba() を使った例
この2番目の例では私は上の例と似たことをしてますが、代わりにそれぞれの上に円を描き、不透明度を上げながら小さいな矩形を描いています。塗りつぶしと輪郭のスタイルは個別に設定することができるので、rgba() を使うとより扱いやすく柔軟性があります。
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
// 背景を描く
ctx.fillStyle = 'rgb(255,221,0)';
ctx.fillRect(0,0,150,37.5);
ctx.fillStyle = 'rgb(102,204,0)';
ctx.fillRect(0,37.5,150,37.5);
ctx.fillStyle = 'rgb(0,153,255)';
ctx.fillRect(0,75,150,37.5);
ctx.fillStyle = 'rgb(255,51,0)';
ctx.fillRect(0,112.5,150,37.5);
// 半透明な矩形を描く
for (i=0;i<10;i++){
ctx.fillStyle = 'rgba(255,255,255,'+(i+1)/10+')';
for (j=0;j<4;j++){
ctx.fillRect(5+i*14,5+j*37.5,14,27.5)
}
}
}
[編集] 線のスタイル
線のスタイルを許すいくつかのプロパティがあります。
lineWidth = value
lineCap = type
lineJoin = type
miterLimit = value
私は詳細にこれらについて説明することができますが、おそらく下の例を見ることでより明らかにされるでしょう。
[編集] lineWidth の例
このプロパティは現在の線の太さを設定します。値は正数でなければなりません。標準ではこの値は 1.0 単位に設定されています。
線の幅は与えられた軌跡を中心にした太さです。言い換えると、パスの両側に、指定した線の幅の半分だけ描画領域が広げられます。Canvas の座標系はピクセル数を直接参照しないため、水平線と垂直線をはっきりさせるための特別な配慮が必要です。
下の例では、線幅を太くしながら 10 本の直線が描画されます。左端の線は 1.0 単位幅ですが、その他の太さが奇数幅の線はパスの配置の仕方によって、はっきりした線になりません。
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
for (i = 0; i < 10; i++){
ctx.lineWidth = 1+i;
ctx.beginPath();
ctx.moveTo(5+i*14,5);
ctx.lineTo(5+i*14,140);
ctx.stroke();
}
}
はっきりした線を得るには、パスがどのように描かれるかを理解する必要があります。下図のグリッドは canvas の座標を表しています。グリッド線の間の升目は実際のスクリーン上のピクセルです。左端の図では、(2,1) から (5,5) までの四角形が塗りつぶされています。この領域 (明るい赤色の範囲) は全体がピクセルの境界線上にあるので、塗りつぶされた四角形の縁は、はっきりしたものになります。
線の太さが 1.0 で (3,1) から (3,5) までのパスを考えた場合、中央の図のようになります。実際に塗りつぶされる領域 (暗い青色) は中間からパスの両側のピクセルまで広がり、考えていたパスに近いものが描画されます。つまり、これらのピクセルのみが部分的に薄くされ、領域全体 (明るい青色と暗い青色) の半分が、実際に描く色として暗く塗りつぶされます。これが先のコード例の 1.0 幅の線で起こったことです。
これを修正するには、とても正確にパスを作成しなければなりません。知ってのとおり、1.0 幅の線は線幅を単位の半分からパスの両側まで広げます。右端の図は、(3.5,1) から (3.5,5) までのパスを作成した場合の結果です。最終的に 1.0 幅の線は完成し、単一ピクセルの垂直線を正確に塗りつぶすことができました。
偶数幅の線については、二等分してもピクセルが整数になるので、パスの位置をピクセルの半分にする代わりに、ピクセル間((3,1) から (3,5) まで) にあるパスにすることができます。また、私たちの垂直線の例では、Y の位置がグリッド線の整数の位置を参照していることに気付くと思います。そうでなければ、線の終端のピクセルに半分適用されることが分かります。
スケーラブルな 2D 画像ではじめに作業するときは、少しばかりの困難を伴います。ピクセルグリッドやパスの位置に注意を払うと、無頓着な大きさの変更や変形があっても正しく見えるように描画することができます。1.0 幅の垂直線を正しい位置で描画すると、2 倍に拡大しても、はっきりした 2 ピクセルの線になり、正しい位置に現れます。
[編集] lineCap の例
lineCap プロパティはすべての線の終点の描かれ方を定義します。このプロパティには 3 つの取り得る値があり、それらは: butt とround と square。標準ではこのプロパティは butt に設定されています。
この例では、lineCap プロパティの異なる値で3本の線を描いています。また、私は 3 つの正確な違いを見るために 2 つのガイドを加えました。これらの線のそれぞれは、正確にこれらのガイドで始まり、終わっています。
左の線は標準の butt オプションを使用します。 あなたはそれが完全にガイドに平坦で描いてあるのに気付くでしょう。
2番目がround オプションを使用するように設定されます。これは線の幅の半分の半径の半円を終点に加えます。
右の線は square のオプションを使用します。これは線幅と等しい幅と半分の高さの長方形を加えます。
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
var lineCap = ['butt','round','square'];
// ガイドを描く
ctx.strokeStyle = '#09f';
ctx.beginPath();
ctx.moveTo(10,10);
ctx.lineTo(140,10);
ctx.moveTo(10,140);
ctx.lineTo(140,140);
ctx.stroke();
// 線を描く
ctx.strokeStyle = 'black';
for (i=0;i<lineCap.length;i++){
ctx.lineWidth = 15;
ctx.lineCap = lineCap[i];
ctx.beginPath();
ctx.moveTo(25+i*50,10);
ctx.lineTo(25+i*50,140);
ctx.stroke();
}
}
[編集] lineJoin の例
lineJoin プロパティは図形の中の2つの線が互いにどのように接続するかを定義します。このプロパティには 3 つの取り得る値があります: round と bevel、miter。標準でこのプロパティは miter に設定されています。
再び私はそれぞれ異なった lineJoin プロパティの設定の 3 本のパスを描いています。上のパスは round オプションを使っています。この設定は図形の角を丸めます。まるめらえた角の半径は線の幅に等しいです。2 番目の線は bevel オプションを使い、下の線は miter オプションを使っています。miter を設定した時、線は、1点で接続するために外の縁を広げることで接続されます。この設定は下で説明されている miterLimit プロパティによって影響されます。
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
var lineJoin = ['round','bevel','miter'];
ctx.lineWidth = 10;
for (i=0;i<lineJoin.length;i++){
ctx.lineJoin = lineJoin[i];
ctx.beginPath();
ctx.moveTo(-5,5+i*40);
ctx.lineTo(35,45+i*40);
ctx.lineTo(75,5+i*40);
ctx.lineTo(115,45+i*40);
ctx.lineTo(155,5+i*40);
ctx.stroke();
}
}
[編集] miterLimit プロパティのデモ
前の例で見たように、miter オプションで 2つの線をつなぐとき、2つの接続する外側の縁はそれらが出会う点で広げられます。互いが大きい角度にある線には、この点が内側の接続点から遠くにはありません。 しかしそれぞれの間の角度が減少するとき、これらの点の間の距離(斜め継ぎの長さ miter length)は指数関数的に増えます。
miterLimit プロパティは外の接続点を内面の接続点からどの程度遠くに置くことができるかを決定します。 2つの線がこの値を超えると、ベヴェル接続(bevel join)が描かれるでしょう
私は、miterLimit を動的に設定できる小さなデモをつくり、これがどう canvas の図形に影響するかを見えます。 青の線はジグザグなパターンのそれぞれの線の始点と終点がどこにあるかを示します。
[編集] グラデーション
普通の描画プログラムのように、線形と円形グラデーションを使った図形を塗りつぶし、輪郭を描くことができます。以下のメソッドの1つを使って canvasGradient オブジェクトを作ります。このオブジェクトを fillStyle か strokeStyle プロパティに割り当てることができます。
createLinearGradient(x1,y1,x2,y2)
createRadialGradient(x1,y1,r1,x2,y2,r2)
createLinearGradient はグラデーションの始点(x1,y1) と終点(x2,y2) を表す4つの引数をとります。
createRadialGradientメソッドは6つの引数をとります。 最初の3つは座標(x1,y1) 半径 r1 の円を定義し、2つめは座標(x2,y2) 半径 r2 の円を定義を定義します。
var lineargradient = ctx.createLinearGradient(0,0,150,150); var radialgradient = ctx.createRadialGradient(75,75,0,75,75,100);
一度 canvasGradient オブジェクトを作ったなら、addColorStop メソッドを使って色を割り当てることができます。
addColorStop(position, color)
このメソッドは2つの引数をとります。position は 0.0 から 1.0 の間の数値でなければならず、グラデーションの色の相対位置を定義します。例えばこれを 0.5 に設定すると色は正確にグラデーションの中心に置かれます。color 引数は CSS 色を表す文字列でなければなりません(つまり #FFF、 rgba(0,0,0,1)、等)。
あなたは必要とするだけグラデーションに色ストップを追加することができます。以下はとても単純な白から黒への線形グラデーションです。
var lineargradient = ctx.createLinearGradient(0,0,150,150); lineargradient.addColorStop(0,'white'); lineargradient.addColorStop(1,'black');
[編集] createLinearGradient の例
この例では、2つの異なるグラデーションを作ります。はじめに背景グラデーションを作ります。あなたが見れるように、同じ場所に2つの色を割り当てています。こうすることでとても鋭い色の変化が得られます。この場合では白から緑です。通常、どの順番で色ストップを定義するかは問題ではありませんが、ここであげるような特別な場合ではそれは明確です。もしあなたが常に出現させたい順に値を割り当てるならこれは問題にならないでしょう。
2番目のグラデーションでは、厳密に必要ではないので、始点の色(位置 0.0)を割り当てていません。自動的に黒を位置 0.5 に割り当てることで、グラデーションは始めからこのストップ、つまり黒、までのグラデーションとなります。
ここで見た通り、 strokeStyle と fillStyle プロパティは canvasGradient オブジェクトを正しい入力として受け付けます。
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
// グラデーションを作る
var lingrad = ctx.
createLinearGradient(0,0,0,150);
lingrad.addColorStop(0, '#00ABEB');
lingrad.addColorStop(0.5, '#fff');
lingrad.addColorStop(0.5, '#26C000');
lingrad.addColorStop(1, '#fff');
var lingrad2 = ctx.createLinearGradient(0,50,0,95);
lingrad2.addColorStop(0.5, '#000');
lingrad2.addColorStop(1, 'rgba(0,0,0,0)');
// グラデーションを塗りとストロークのスタイルに割り当てる
ctx.fillStyle = lingrad;
ctx.strokeStyle = lingrad2;
// 図形を描く
ctx.fillRect(10,10,130,130);
ctx.strokeRect(50,50,50,50);
}
[編集] createRadialGradient の例
この例では、4つの異なる円形グラデーションを定義しています。グラデーションの始点と終点を管理するので、例えば Photshop でみるような 'クラシック' な線形グラデーションより複雑な効果を実現することができます。(つまり、グラデーションが図形の外に円形に広がる1つの中心点をもったグラデーション)
この場合、私は、球の 3D 効果を実現するためにわずかに終点から出発点を補正しました。予測困難な奇妙な効果をもたらすことになるので、内側と外側の円が重ね合わさることを避けることは大変良いことです。
それぞれの 4 つのグラデーションにおける最後の色ストップは完全な透明色を使用しています。あなたがこれから前の色ストップまでの素晴らしい変化が欲しいなら、両方の色は等しくすべきです。 これは、私が2つの異なったCSS の色のメソッドを使用したのでコードからそれほど明白ではありませんが、最初のグラデーションでは #019F62 = rgba(1,159,98,1) です。
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
// グラデーションを作る
var radgrad = ctx.createRadialGradient(45,45,10,52,50,30);
radgrad.addColorStop(0, '#A7D30C');
radgrad.addColorStop(0.9, '#019F62');
radgrad.addColorStop(1, 'rgba(1,159,98,0)');
var radgrad2 = ctx.createRadialGradient(105,105,20,112,120,50);
radgrad2.addColorStop(0, '#FF5F98');
radgrad2.addColorStop(0.75, '#FF0188');
radgrad2.addColorStop(1, 'rgba(255,1,136,0)');
var radgrad3 = ctx.createRadialGradient(95,15,15,102,20,40);
radgrad3.addColorStop(0, '#00C9FF');
radgrad3.addColorStop(0.8, '#00B5E2');
radgrad3.addColorStop(1, 'rgba(0,201,255,0)');
var radgrad4 = ctx.createRadialGradient(0,150,50,0,140,90);
radgrad4.addColorStop(0, '#F4F201');
radgrad4.addColorStop(0.8, '#E4C700');
radgrad4.addColorStop(1, 'rgba(228,199,0,0)');
// 図形を描く
ctx.fillStyle = radgrad4;
ctx.fillRect(0,0,150,150);
ctx.fillStyle = radgrad3;
ctx.fillRect(0,0,150,150);
ctx.fillStyle = radgrad2;
ctx.fillRect(0,0,150,150);
ctx.fillStyle = radgrad;
ctx.fillRect(0,0,150,150);
}
[編集] パターン
前のページの例の1つで、私は画像のパターンを作成するために一連のループを使用しました。しかし、はるかに簡単な方法があります: createPattern メソッドです。
createPattern(image,type)
このメソッドは2つの引数をとります。image は Image オブジェクトか他の canvas 要素への参照のどちらかです。type は次の値の内のどれかの文字列でなければなりません: repeat と repeat-x、repeat-y、 no-repeat。
Image を引数として使用する canvas 要素は Firefox 1.5 (Gecko 1.8) では動きません。このメソッドを使用して、上で見たグラデーションのメソッドと良く似た Pattern オブジェクトを作成します。
一度パターンを作ってしまえば、それをfillStyle や strokeStyle プロパティに割り当てられます。
var img = new Image(); img.src = 'someimage.png'; var ptrn = ctx.createPattern(img,'repeat')
注意: drawImage メソッドと異なり、あなたは使用する画像がこのメソッドを呼ぶ前に読み込まれているか確認しなくてはなりません。さもなければそのパターンは不正に描かれるかもしれません。
注意: Firefox は現在 repeat プロパティのみをサポートしています。それ以外を割り当てても何の変化も見られないでしょう。
[編集] createPattern の例
この最後の例で、fillStyle に割り当てるパターンを作りました。注意する価値がある唯一のものは Image オブジェクトの onload ハンドラの利用です。これはパターンに画像が割り当てられる前に、画像が読み込まれていることを確実にするためのものです。
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
// パターンとして使う新しい image オブジェクトを作る。
var img = new Image();
img.src = 'images/wallpaper.png';
img.onload = function(){
// パターンを作る
var ptrn = ctx.createPattern(img,'repeat');
ctx.fillStyle = ptrn;
ctx.fillRect(0,0,150,150);
}
}











