HTML+JavaScriptだけでブラウザに図形描画(2) - Canvas API -

2012年8月6日(月)
山田 祥寛(YAMADA, Yoshihiro)

今回は、前回の記事に続いてCanvas APIに関するAPIをまとめます。

[第3回目次]
  • TIPS 018:キャンバスにテキストを描画する
  • TIPS 019:文字列の横幅を計算する
  • TIPS 020:図形に影効果を追加する
  • TIPS 021:線形グラデーションを表現する
  • TIPS 022:円形グラデーションを表現する
  • TIPS 023:キャンバスに画像を貼り付ける
  • TIPS 024:画像の一部を切り出して貼り付ける
  • TIPS 025:画像を繰り返し表示する
  • TIPS 026:画像の特定領域を切り抜く
  • TIPS 027:特定の座標がパス領域に含まれるかを判定する

サンプル一式は、会員限定特典としてダウンロードできます。記事末尾をご確認ください。

TIPS 018:キャンバスにテキストを描画する

キャンバスにテキストを描画するには、strokeText/fillTextメソッドを利用します。

[リスト1]キャンバスに塗りつぶし文字、枠文字を描画するコード(text.html)

window.addEventListener('DOMContentLoaded',
  function() {
    if (HTMLCanvasElement) {
      var cv = document.querySelector('#cv');
      var c = cv.getContext('2d');
      c.strokeStyle = '#F0F'; // 枠線の色
      c.fillStyle = '#00F'; // 塗りつぶし色
      // テキスト描画の設定(1)
      c.font = 'italic bold 40pt sans-serif';	// フォント
      c.textAlign = 'center';	// テキストの位置
      // 塗りつぶしテキストを描画(2)
      c.fillText('ThinkIT', 80, 50);
      // 枠線のみのテキストを描画
      c.strokeText('ThinkIT', 120, 100);
    }
  }
);
図1:塗りつぶしテキストと枠線テキストを描画(クリックで拡大)

fillTextメソッドで塗りつぶしテキストを、strokeTextメソッドで枠線のみのテキストを、それぞれ描画できます。

[構文]fillText/strokeTextメソッド

  • fillText(文字列, X座標, Y座標)
  • strokeText(文字列, X座標, Y座標)

サンプルでは、互いの座標を変えているので枠線テキストと塗りつぶしテキストとを単体で描画していますが、座標を一致させれば「枠線付きの塗りつぶしテキスト」を描画することもできます。

図2:枠線付きの塗りつぶしテキストを描画(クリックで拡大)

fontプロパティでテキストのフォントを、textAlignプロパティで表示位置も指定できます。fontプロパティの値は、CSSのfontプロパティと同じく、以下の順序で値を列挙します。

表1:fontプロパティの設定

プロパティ 概要
font-style 斜体(normal/italic/oblique)
font-weight ボールド(normal/bold/lighter/bolder/100、200...900)
font-size フォントサイズ
/line-height 行の高さ(値の前に「/」が必要)
font-family フォントの種類

textAlignプロパティで設定できる値は、以下の図のとおりです。

図3:text Alignプロパティで設定できるテキストの表示位置(クリックで拡大)

Canvas APIは、fillText/strokeTextプロパティで指定されたX座標とtextAlignプロパティの値によって、文字の表示位置を決定します。上の図では、赤線がfillText/strokeTextプロパティで指定されたX座標を表すものとします。

TIPS 019:文字列の横幅を計算する

measureTextメソッドを利用することで、ある文字列がキャンバスの中で占めるサイズを求めることができます。文字列の幅に応じて、関係する図形の位置を決定したいという場合などに利用できるでしょう。

[リスト2]文字列の横幅を求めるコード(measure.html)

window.addEventListener('DOMContentLoaded',
  function() {
    if (HTMLCanvasElement) {
      var cv = document.querySelector('#cv');
      var c = cv.getContext('2d');
      // 塗りつぶし色やフォント、表示位置の設定
      c.fillStyle = '#00F';
      c.font = 'italic bold 40pt serif';
      c.textAlign = 'left';
      // 文字列「Think IT」の横幅を計算
      var str = 'Think IT';
      window.alert(c.measureText(str).width);
      c.fillText(str, 80, 50);
    }
  }
);
図4:文字列の横幅をダイアログボックスで表示

measureTextメソッドは、指定された文字列のサイズをTextMetricsオブジェクトとして返します。よって、横幅を求めるには、そのwidthプロパティにアクセスしなければならない点に注意してください。

現時点では、TextMetricsオブジェクトはwidthプロパティしか持ちませんが、将来的にはheightプロパティ(高さ)のような文字列のサイズに関わる情報を扱えるようになると思われます。

TIPS 020:図形に影効果を追加する

図形に影を付与するには、shadowXxxxxプロパティを利用します。

[リスト3]多角形に影を追加するコード(shadow.html)

window.addEventListener('DOMContentLoaded',
  function() {
    if (HTMLCanvasElement) {
      var cv = document.querySelector('#cv');
      var c = cv.getContext('2d');
      // 多角形を描画
      c.beginPath();
      c.moveTo(15, 15);
      c.lineTo(30, 250);
      c.lineTo(250, 200);
      c.lineTo(280, 130);
      c.lineTo(250, 80);
      c.closePath();
 
      // 背景色&影効果を設定
      c.fillStyle = '#F0F';
      c.shadowColor = '#000';
      c.shadowOffsetX = 7;
      c.shadowOffsetY = 7;
      c.shadowBlur = 15;
      c.fill();
    }
  }
);
図5:多角形の背後に影(クリックで拡大)

影効果に関わるプロパティには、以下のようなものがあります。

表2:影効果に関わるプロパティ

プロパティ 概要
shadowColor 影の色
shadowOffsetX 影のズレ(水平方向)
shadowOffsetY 影のズレ(垂直方向)
shadowBlur 影のぼかし

shadowBlurプロパティには、0以上の値を指定できます。値が大きくなればなるほど、ぼかしの程度は大きくなります。

TIPS 021:線形グラデーションを表現する

fillStyle/strokeStyleプロパティには、単純な色だけでなく、グラデーション効果を指定することもできます。以下は、多角形に線形グラデーションを適用する例です。線形グラデーションとは、上→下、左→右など指定した方向に直線状に色が変化するグラデーションのことを言います。

[リスト4]多角形に線形グラデーションを適用するコード(gradient.html)

window.addEventListener('DOMContentLoaded',
  function() {
    if (HTMLCanvasElement) {
      var cv = document.querySelector('#cv');
      var c = cv.getContext('2d');
      // 多角形を描画
      c.beginPath();
      c.moveTo(15, 15);
      c.lineTo(30, 250);
      c.lineTo(250, 200);
      c.lineTo(280, 130);
      c.lineTo(250, 80);
      c.closePath();
 
      // 線形グラデーションを設定(1)
      var g = c.createLinearGradient(15, 15, 280, 130);
      // グラデーションで利用する色を設定(2)
      g.addColorStop(0, '#F00');
      g.addColorStop(0.5, '#0F0');
      g.addColorStop(1, '#00F');
      // 塗りつぶし色としてグラデーションをセット(3)
      c.fillStyle = g;
      c.fill();
    }
  }
);
図6:多角形の背景として赤→緑→青と変化するグラデーションを適用(クリックで拡大)

グラデーション設定は、CanvasGradientというオブジェクトで表します。CanvasGradientオブジェクトを生成するには、createLinearGradientメソッドを利用します。

[構文]createLinearGradientメソッド

  • createLinearGradient(x1, y1, x2, y2)

これで始点(x1, y1)から終点(x2, y2)への線形グラデーションを表現できます。

もっとも、この時点ではグラデーションの開始点と終了点が決まったにすぎません。続いて、グラデーションで利用する色を指定します。これを行っているのが、addColorStopメソッドです(2)。

[構文]addColorStopメソッド

  • addColorStop(オフセット, 色)

オフセットは色を変化させる位置を表す0~1の数値です。0.0が始点、1.0が終点を表します。サンプルであれば、#F00(赤)で始まり、中間で#0F0(緑)を経て、終点で#00F(青)となるようなグラデーションを定義しています。もちろん、addColorStopメソッドを連ねることで、より複雑なグラデーションも表現できます。

後はできあがったCanvasGradientオブジェクトをfillStyleプロパティにセットすることで(3)、塗りつぶし色としてグラデーションが適用されます。

TIPS 022:円形グラデーションを表現する

リスト5の1行目を以下のように書き換えることで、円形グラデーションも表現できます。円形グラデーションとは、指定された2つの円の間で円状に色が変化するグラデーションのことです。

[リスト5]多角形の背景に円形グラデーションを適用するコード(gradient.html)

var g = c.createRadialGradient(60, 80, 10, 140, 140, 120);
g.addColorStop(0, '#F00');
g.addColorStop(0.5, '#0F0');
g.addColorStop(1, '#00F');
c.fillStyle = g;
図7:多角形の背景として赤→緑→青と円形に変化するグラデーションを適用(クリックで拡大)

円形グラデーションを定義するには、createLinearGradientメソッドの代わりにcreateRadialGradientメソッドを利用します。

[構文]createRadialGradientメソッド

  • createRadialGradient(x1, y1, r1, x2, y2, r2)

これによって、中心(x1, y1)、半径r1の円から中心(x2, y2)、半径r2の円に向けた円形グラデーションを表現できます。グラデーションの始点/終点が設定できたら、後は、addColorメソッドで色の変化を表し、fillStyleプロパティにグラデーションをセットするという以降の流れは、線形グラデーションの場合と同じです。

TIPS 023:キャンバスに画像を貼り付ける

キャンバスに画像を貼り付けるには、drawImageメソッドを使います。

[リスト6]画像をキャンバスに貼り付けるコード(image.html)

window.addEventListener('DOMContentLoaded',
  function() {
    if (HTMLCanvasElement) {
      var cv = document.querySelector('#cv');
      var c = cv.getContext('2d');
      // 画像を準備(1)
      var img = new Image();
      img.src = 'wings.jpg';
      // 画像をロードできたら、キャンバスに貼り付け
      img.addEventListener('load', function(e) {
        c.drawImage(img, 70, 100, 180, 58);
      });
    }
  }
);
図8:指定された画像をキャンバスに反映(クリックで拡大)

キャンバスに画像を貼り付けるには、まずImageオブジェクトを準備します(1)。new演算子でImageオブジェクトをインスタンス化した後、srcプロパティで画像のパスを指定してください。

この後、そのままキャンバスに画像を貼り付けたいところですが、それはできないので要注意です。というのも、画像はスクリプトの処理とは非同期に読み込まれるため、srcプロパティを呼び出した直後に画像が読み込まれているとは限りません。そこで、画像が正しく読み込まれた(=loadイベントが発生した)ことを確認した上で、以降の処理を行う必要があるのです。

それが(2)のコードです。loadイベントリスナの中でキャンバスへ画像を貼り付けているのはdrawImageメソッドの役割です。

[構文]drawImageメソッド

  • drawImage(画像オブジェクト, x, y, width, height)

引数のx/y/width/heightは、図8の内容に対応しています。画像をオリジナルサイズのままで貼り付けたいならば、引数width/heightは省略しても構いません。

TIPS 024:画像の一部を切り出して貼り付ける

drawImageメソッドを利用することで、元画像の一部を切り出した上で、貼り付けることもできます。リスト7の3行目を、以下のように書き換えてください。

[リスト7]画像の一部を切り出した上で貼り付けるコード(image.html)

img.src = 'wings.jpg';
img.addEventListener('load', function(e) {
  c.drawImage(img, 0, 0, 54, 60, 100, 100, 70, 75);
});
図9:画像の一部だけを切り出した(クリックで拡大)

以下に、サンプルで利用しているdrawImageメソッドの構文を示します。

[構文]drawImageメソッド

  • drawImage(画像オブジェクト, x1, y1, w1, h1, x2, y2, w2, h2)

これによって、元画像から座標(x1, y1)を基点にw1×h1の区画を切り取り、キャンバスの座標(x2, y2)に対してw2×h2のサイズで貼り付ける、という意味になります。引数と実際の画像との対応関係は、図9も参照してください。

TIPS 025:画像を繰り返し表示する

fillStyle/strokeStyleプロパティには、パターンを指定することもできます。パターンとは、図形(または枠線)を塗りつぶす際に利用する画像の繰り返しのことを言います。

[リスト8]指定された画像を上下左右に繰り返し貼り付けるコード(pattern.html)

window.addEventListener('DOMContentLoaded',
  function() {
    if (HTMLCanvasElement) {
      var cv = document.querySelector('#cv');
      var c = cv.getContext('2d');
      // 画像を準備
      var img = new Image();
      img.src = 'wings.jpg';
      // 画像が準備できたら、以降の処理を実行
      img.addEventListener('load', function(e) {
        // パターンを作成(1)
        var p = c.createPattern(img, 'repeat');
        // 塗りつぶし色としてパターンをセット(2)
        c.fillStyle = p;
        c.fillRect(0, 0, 400, 300);
      });
    }
  }
);
図10:画像が上下左右に敷き詰められた(クリックで拡大)

パターンは、CanvasPatternオブジェクトで表します。CanvasPatternオブジェクトは、createPatternメソッドで作成できます(1)。

[構文]createPatternメソッド

  • createPattern(画像, パターンの種類)

サンプルでは、画像としてImageオブジェクトを指定していますが、その他、Video/HTMLCanvasElementなどのオブジェクトも指定できます。

パターンの種類は、画像をどのように敷き詰めるかを表すものです。指定できるパターンには、以下のようなものがあります。

設定値 概要
repeat 上下左右双方に繰り返し
repeat-x 水平方向にのみ繰り返し
repeat-y 垂直方向にのみ繰り返し
no-repeat 繰り返さない

パターンを作成できたら、後はこれをfillStyleプロパティにセットすることで、塗りつぶし色としてパターンが適用されます(2)。ここではキャンバス全体をパターンで塗りつぶしたいので、fillRectメソッドでキャンバスと同じ大きさの四角形を描画しています。

TIPS 026:画像の特定領域を切り抜く

clipメソッドを利用することで、キャンバスの特定の領域だけを切り抜きます(これをクリッピング領域と言います)。clipメソッドが呼び出された場合、以降は、クリッピング領域に対してのみ図形が描画できるようになります。

[リスト9]指定された円の領域内のみ画像を描画するコード(clip.html)

window.addEventListener('DOMContentLoaded',
  function() {
    if (HTMLCanvasElement) {
      var cv = document.querySelector('#cv');
      var c = cv.getContext('2d');
      // 円を描画
      c.beginPath();
      c.arc(250, 180, 100, 0, 2 * Math.PI, false);
      c.stroke();
      // 現在のパスに基づいて、クリッピング領域を定義
      c.clip();
 
      // 画像を準備&貼り付け(2)
      var img = new Image();
      img.src = 'rin.jpg';
      img.addEventListener('load', function(e) {
        c.drawImage(img, 0, 0, 400, 300);
      });
    }
  }
);
図11:円の内部でのみ画像を描画(クリックで拡大)

この例では、arcメソッドで円のパスを定義した後で、clipメソッドを呼び出していますので(1)、以降の画像貼り付け(2)は円で囲まれた領域内に対してのみ反映されることになります。

サンプルでは、strokeメソッドで枠囲みしていますが、単にクリッピング領域を定義したいだけであれば、strokeメソッドは省略しても構いません。

クリッピング領域とパスを組み合わせることで、より複雑な画像の貼り付けが可能になります。

TIPS 027:特定の座標がパス領域に含まれるかを判定する

isPointInPathメソッドを利用することで、指定された座標が現在のパス領域に含まれるかを判定できます。

[リスト10]座標がパスに含まれるかを確認するコード(inpath.html)

window.addEventListener('DOMContentLoaded',
  function() {
    if (HTMLCanvasElement) {
      var cv = document.querySelector('#cv');
      var c = cv.getContext('2d');
      // 多角形を描画
      c.beginPath();
      c.moveTo(15, 15);
      c.lineTo(30, 250);
      c.lineTo(250, 200);
      c.lineTo(280, 130);
      c.lineTo(250, 80);
      c.closePath();
      c.stroke();
     
      //c.beginPath();
 
      // 座標(100, 100)がパスの領域内に含まれるかをチェック
      if (c.isPointInPath(100, 100)) {
        window.alert('座標はパスに含まれます。');
      } else {
        window.alert('座標はパスの領域外です。');
      }
    }
  }
);
図12:パスの領域内にある旨をダイアログ表示

isPointInPathメソッドの構文は、以下のとおりです。

[構文]isPointInPathメソッド

  • isPointInPath(X座標, Y座標)

isPointInPathメソッドは結果をtrue(含まれる)、false(含まれない)で返します。

なお、isPointInPathメソッドが判定するのは、現在のパスに対してです。よって、16行目をコメントインし、新たなパスを開始すると、いわゆるパスで表される領域がない状態になりますので、結果は「座標はパスの領域外です。」となります。

  • Canvas APIを使ってブラウザに図形を描画するサンプル

著者
山田 祥寛(YAMADA, Yoshihiro)
WINGSプロジェクト

有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表:山田祥寛)。おもな活動は、Web開発分野の書籍/雑誌/Web記事の執筆。ほかに海外記事の翻訳、講演なども幅広く手がける。2011年3月時点での登録メンバは36名で、現在もプロジェクトメンバーを募集中。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています