Logical Thinking and Programming in 2012 Autumn
Minohara Class


第7回 オブジェクトとキャンバス描画

オブジェクト

これまでなんとなくオブジェクトと言うものを使ってきました。オブジェクトとは値の集合です。値は、数字だったり、文字列だったり、関数だったりします。ここではもう少し詳しくオブジェクトをみていきましょう。

オブジェクト

上で述べたように、オブジェクトは値の集合です。例えば、名前というオブジェクトは、日本の場合は「性」と「名」で出来ています。また、位置というオブジェクトは「緯度」と「経度」で出来ているかもしれません。更には、人を表すオブジェクトは、先に出てきた「名前」というオブジェクト、つまり「性」と「名」を含むオブジェクトと「年齢」「性別」と表現することができます。

名前を表すオブジェクト

名前を表すオブジェクト

位置を表すオブジェクト

位置を表すオブジェクト

人を表すオブジェクト

人を表すオブジェクト

オブジェクトリテラル

JavaScriptにおいて、実際の値を使ってオブジェクトを表現するためにはオブジェクトリテラルが用いられます。オブジェクトリテラルは次のような書式を持ちます。

  {プロパティ名: 値, プロパティ名: 値, ...}

プロパティ名というのは、オブジェクトの中に存在する個々の値につけられた名前です。例えば先ほどの名前をオブジェクトリテラルで書くと次のように書く事ができます。ここで「"慶應"」「"太郎"」のように値がダブルクォートで囲まれているのは値が文字列だからです。

	{familyName: "慶應", firstName: "太郎"}

プロパティ名は空白を含む任意の文字列を利用する事ができます。空白を含む場合にはプロパティ名をクォートやダブルクォートで囲む必要があります。しかし、後に述べるオブジェクトの参照、つまり値の使い方の際に制限がでますので、空白や「-」などの利用は避けておいた方が無難です。漢字やひらがなを使う事も出来ますが、止めておいた方がよいでしょう。

	{"family name": "慶應", "first-name": "太郎"}

また、人のようにオブジェクトを内包したオブジェクトの場合は下記のように記述することが可能です。ここでは、「name」というプロパティは「{familyName: "慶應", firstName: "太郎"}」というオブジェクトにひもづけられていますので気をつけてください。

	{name: {familyName: "慶應", firstName: "太郎"}, age: 19, sex: "male"}

オブジェクトリテラルによるオブジェクトの生成

オブジェクトが入った変数を作るのは簡単です。予め初期化されたオブジェクトを作るためには下記のように記述します。

 var 変数名 = {プロパティ名: 値, プロパティ名: 値, ...};

例えば、人のオブジェクトを生成するためには下記のようにすれば良いわけです。読みやすいように改行しておきましたが、改行はあっても無くても大丈夫です。見やすいように書くよう心がけましょう。

	var human = {
	  name: {familyName: "慶應", firstName: "太郎"},
	  age: 19,
	  gender: "male"
	};

オブジェクトの参照

オブジェクトを変数に入れたら、当然それを使う事を考えなければなりません。プログラムの中でオブジェクトの中の値を参照するためには次のように表現します。

 オブジェクト名.プロパティ名
 オブジェクト名["プロパティ名"]
 オブジェクト名['プロパティ名']

どの行も同じ値を表していますが、1行目の表現方法の場合、注意が必要です。というのもプロパティ名が「123」という数字だったり、「first-name」の様にマイナスを含んでいたりして、変数名の命名規則に則っていない場合は問題がおきます。これが、上で空白やマイナスなどの文字列は使わない方が無難だと言った理由です。

さて、では実際のプログラムの中ではどのように利用するのでしょうか。下記をみてください。下記は、「human」というオブジェクトを生成し、そのオブジェクトが含むそれぞれのプロパティの値を表示させた例です。例では、いろいろな表現があることを示す為に、「human.name.familyName」「human.name["firstName"]」など異なった表現を使っていますが、不必要に表現を混ぜないことがプログラムの見やすさを向上させます。

	/*
	 *  オブジェクトの生成
	 */
	var human = {
	  name: {familyName: "慶應", firstName: "太郎"},
	  age: 19,
	  gender: "male"
	};

	/*
	 *  プロパティの表示
 	*/
	document.write("性: " + human.name.familyName + "<br />");
	document.write("名: " + human.name["firstName"] + "<br />");
	document.write("年齢: " + human.age + "<br />");
	document.write("性別: " + human['gender'] + "<br />");

クラスによるオブジェクトの生成の記述の仕方

JavaScriptでは、new演算子というものによってクラスを参照してオブジェクトを生成します。 オブジェクトを生成する「式」は以下のようなものになります。

 new クラス名( )

クラス名は大文字で始まりますから、たとえば「new String( )」というような感じで記述します。 「new」とクラス名の間には、空白が1つ以上必要になります。 後で説明するメソッド呼出しに似ているのですが、このString( )いうクラス名と同じ名前をもつメソッドは、 オブジェクトを作るときに呼ばれる特殊なメソッドの1つです。JavaScript言語では、コンストラクタ(Constructor)と 呼ばれています。

new 指定したクラス名( )」という記述があるのは、 クラスとして記述されたプログラムに対して実際のオブジェクトを1つ生成する意味を持っています。 たとえば、次のような形で記述します。 オブジェクト指向言語では、特定のクラスに属するオブジェクトのことをインスタンス(Instance)と 呼ぶこともあります。

		
	new Date( );  // Dateというクラスのオブジェクトを1つ作成する
		

オブジェクトを生成するときも引数(パラメータ)を指定することもできます。 そのときは、以下のように記述します。 ブロックと紛らわしくて申し訳ないのですが、{ A }…は、Aという内容の0回以上の繰返しだと思って下さい。

 new クラス名( パラメータ {, パラメータ }… )

他の例を見てみます。 生成時に渡されるパラメータは、サブウィンドウのタイトルを示しています。

		
	new Date( 2012, 11, 31 );  // 2012年12月21日で日付を生成する
		

Dateクラスのオブジェクトを作ってみる

Dateクラスのオブジェクトを作るのは、パラメータを何も指定しない(現在の日時になる)か、 日付を表す3つの値(年,月-1,日)を指定するか、 日時を表す6つの値(年,月-1, 日, 時, 分, 秒)を指定します。 月については、1〜12ではなく、1つ値が引かれた0〜11を指定します。 Dateクラスのオブジェクトを作成する場合は、以下のような形でコンストラクタにパラメータを渡します。

 new Date( ) あるいは
 new Date( 年,月-1,日 )  あるいは
 new Date( 年,月-1, 日, 時, 分, 秒 )  あるいは
 new Date( UTCミリ秒 )

分,秒は省略されても構いません。その場合は、0分0秒が仮定されます。また、最後にミリ秒を追加しても構いません。それ以外に年月日時分秒を表す文字列を1つ与えて作成することもできます。 4つ目のコンストラクタは、1970年1月1日0時0分0秒からの経過ミリ秒を使って日付を設定するものです。


	new Date(  )     //  実行したときの日時
	new Date( 1999, 11, 31 )     //  1999年12月31日、時間は0時0分0秒になる。
	new Date( 2001, 0, 1, 3, 45, 23 )     // 2001年1月1日、3時45分23秒
		

作られたオブジェクトへの参照

生成されたオブジェクトは、そのままでは非常に短命で、すぐに消去されてしまいます。 そのために、作ったオブジェクトを参照するための変数を用意しなければなりません。 そのための変数宣言を行ないます。

 var 変数名 {, 変数名 }… ;

たとえば、次のようにオブジェクトを参照するための変数を宣言することができます。


	var	current;		// Dateクラスのオブジェクトを参照する変数
		

変数は、基本的な値(整数や実数など)を持つためにも使うことは従来のプログラミング言語と共通なのですが、 JavaScript言語ではこのように変数をオブジェクトを参照するためにも用いることができるのです。 変数は、生成されたオブジェクトを指し示すために用いられます。

変数に何らかのオブジェクトを参照させるためには、そのことを代入文を用いて記述する必要があります。 これは、通常の代入文と同じですが、特にコンストラクタを用いて生成された新しいオブジェクトをすぐに参照したい場合は、 次のように記述します。

 変数名 = new クラス名 ( );  あるいは
 変数名 = new クラス名 (パラメータ {, パラメータ }… );

たとえば、次のように記述します。 それぞれ、新しく生成されたオブジェクト(インスタンス)を参照するようになります。


	current =  new   Date(  );		// 新しく生成したインスタンスを参照する
	past = new  Date( 2001, 4, 5 );	// 2001年5月5日を参照
		

オブジェクトの生成と変数の宣言を同じに行ないたいときは、 通常の値を保持する変数と同様に初期値代入の書式を用いることができます。

 var 変数名 = new クラス名 ( );  あるいは
 var 変数名 = new クラス名 (パラメータ {, パラメータ }… );

以下の例は、生成されたオブジェクトを変数で参照すべく、初期値代入を行なった記述です。


	var current =  new   Date(  );
	var past = new  Date( 2001, 4, 5 );
	var future = new Date(  2300, 1, 10 );  // 2300年2月10日
		

これらは、システムが提供しているオブジェクトであるDateオブジェクトを使って、Dateオブジェクトが持つ様々な機能が使える新たなオブジェクトを作り出し、それを変数dateに代入しているものです。Dateオブジェクトには、予めそのメンバとして、toLocaleString()のような関数が定義されており、これらを利用することが出来るのです。つまり、オブジェクトには、本来、関数も格納することができます。

Dateクラスの詳細については、「とほほのJavaScriptリファレンス(Dateクラス)」を見て下さい。

オブジェクト変数の利用方法

では、生成されたオブジェクトを変数で参照することによって、何をするのでしょうか? それは、変数を通じて、そのオブジェクトが持っているフィールド(属性値)やメソッドを利用するためです。 特に、ここではメソッド呼出しの場合について説明します。

 変数名.メソッド名( ) ;   あるいは
 変数名.メソッド名( パラメータ {, パラメータ }…) ;

ここでもピリオド「.」は、日本語の「の」という意味と同じです。変数の指し示すオブジェクト「の」メソッドを 呼び出します。

次の例は、オブジェクトを参照する変数を通じて、Dateクラスのオブジェクトが持つ メソッドを呼び出しています。


	adatetime.setHours( 14 );				// DateクラスのsetHourメソッドを利用
	anothertime.setMinutes( 27 );		// DateクラスのsetMinuteメソッドを利用
		

あるいは、呼び出したメソッドが何かの情報を返してくれることがあります。 そのときには、次のように代入文を使って記述します。

 変数名 = オブジェクトを指す変数名.メソッド名( ) ;   あるいは
 変数名 = オブジェクトを指す変数名.メソッド名( パラメータ {, パラメータ }… ) ;

たとえば、次の記述は、Dateクラスのオブジェクトを指している変数dに対して、 そのカラーの時間成分を整数として受け取っている例です。 コンストラクタで指定した成分に対して、getHoursは時間を、getMinutesは分を、getSecondsは秒を整数値 で返してくれます。 変数の宣言と代入の2行に分けて記述してみました。


	var hour ;
	hour =  d.getHours( );
		

次の記述は、Dateクラスのオブジェクトを指している変数dateに対して、 その分と秒を受け取っている例です。


	var minute =  date.getMinutes( ) ;
	var second =  date.getSeconds( );
		

メソッドが返してきてくれる情報が別のオブジェクトである場合もあります。 この場合には、そのオブジェクトを参照するための変数を宣言しておかなければなりません。 そして、その変数に代入してやります。

 オブジェクトを指す別の変数名 = オブジェクトを指す変数名.メソッド名( ) ;

次のプログラムの例は、Canvasクラスのオブジェクト(変数canvasが示している)に対して、 そのキャンバスのコンテキスト(描画の中身)の情報を返してもらい、contextという 名前の変数で指すようにしています。この変数は、オブジェクトを参照する変数になっています。


	var  context;	
	context = canvas.getContext( );		// getContextというメソッドを呼び出している
		

上の記述は、変数宣言を伴う場合なので、以下のように書いても構いません。


	var  context = canvas.getContext( );		// getContextというメソッドを呼び出している
		

オブジェクトリテラルの項でも出てきますが、オブジェクトが持つプロパティ(変数のように値を持ちます)を参照するのは、次のような形です。ピリオドで区切ります。ピリオドは、「〜の〜」という意味で用いられています。

オブジェクトを指し示す変数で参照しているオブジェクトのプロパティを参照する書式:
 変数名.プロパティ名

たとえば、以下の例はArray( )クラスのオブジェクトを生成し、要素の個数を持つlengthというプロパティを別の変数に代入しています。


	var  namelist = new Array( );
	var  count = namelist.length;
		

1回限りしか使わないオブジェクト

変数で参照しずっと後まで使い続けるオブジェクトもあれば、一回限りの使い捨てのオブジェクトもあります。 そのようなオブジェクトは、 メソッド呼出しのパラメータの部分に生成するための記述だけが行なわれる場合が多いようです。


	window.alert(  new Date(  ) );	// 日付表示のためだけに作られたオブジェクト

		

上の例は、次のように変数を用意すれば分けて書くこともできます。


	var date;
	date = new Date(  );
	window.alert(  date  );
		

オブジェクトの作り方まとめ

ここでは、new演算子を使ってオブジェクトを作る方法を紹介しましたが、 一般的には、JavaScriptではオブジェクトの作り方は、結局次の3つの方法があります。

Dateクラスのインスタンスの主なメソッド

生成されたDataクラスのインスタンスの日時をとってきたり、日時を設定するためのメソッドの主要なものの一覧です。パラメータの部分には仮に変数の名前がついていますが、利用する際には、そこには式が入ると思って下さい。

メソッドの名前(date付き)説明
date.getFullYear( ) 西暦を4桁で返します
date.setFullYear( year ) 西暦を指定されたyearで設定します
date.getMonth( ) 月を返します(0〜11)
date.setMonth( month ) 月を指定されたmonthで設定します
date.getDate( ) 日を返します
date.setDate( date ) 日をdayで設定します
date.getDay( ) 曜日を返します(0…日曜日〜6…土曜日)
date.setDay( day ) 曜日をdateで設定します
date.getHours( ) 時(0〜23)を返します
date.setHours( hour ) 時をhourで設定します
date.getMinutes( ) 分を返します
date.setMinutes( minute ) 分をminuteで設定します
date.getSeconds( ) 秒を返します
date.setSeconds( second ) 秒をsecondで設定します
date.getMilliseconds( ) ミリ秒を返します
date.setMilliseconds( millisec ) ミリ秒をmillisecで設定します

なお、HoursやMonthなどの前にUTCが付いた名前のメソッドもあります。例:getUTCMinutes( )
このようなメソッドは、UTC(協定世界時間)での時間を扱う為に用います。

以下は文字列や数値への変換のための主要なメソッドの一覧です。

メソッドの名前(date付き)説明
date.toString( ) 文字列に変換する(現地時間)
date.toDateString( ) 日付だけ文字列に変換する(現地時間)
date.toTimeString( ) 時間だけ文字列に変換する(現地時間)
date.toLocaleString( ) 現地表記で日時だけ文字列に変換する(現地時間)
date.toLocaleTimeString( ) 現地表記で時間だけ文字列に変換する(現地時間)
date.toGMTString( ) グリニッジ標準時間の文字列に変換する
date.toUTCString( ) 協定世界時間の文字列に変換する
date.getTime( ) 1970年1月1日0時0分0秒(UTC)からの経過ミリ秒数を返す
date.setTime( totalmsec ) 経過ミリ秒数をtotalmsecで設定する
date.getTimezoneOffset( ) 時間帯の差を分で返す

経過ミリ秒数を使えば、次のようにあるイベントを行なったときの経過時間を求めることができます。

	// 経過時間を求める
	var start = new Date();
	// 時間を計りたいイベントをここで行う
	var end = new Date();
	var elapsed = end.getTime() - start.getTime(); // ミリ秒での時間、こちらでも求められる
	

なお、JavaScript 1.5から導入されたDate直属のクラスメソッドとして、nowメソッドがあります。こちらを使えば、次のようにして経過時間を求めることができます。

	starttime = Date.now( );
	// 時間を計りたいイベントをここで行う
	endtime = Date.now( );
	var elapsed = endtime - starttime; // ミリ秒での時間
	

☆練習問題7-1

  1. 2つの日付を設定します。その2つの日付の間がどれくらいの時間が経過していたのか、時で表示しなさい。
  2. ダイアログを表示します。そのダイアログが表示されていた経過時間を表示しなさい。 ただし、経過時間は「時:分:秒」という形で表示します。ElapsedTime.htmlというファイル名で。

canvasと描画

canvas要素について説明します。

canvas要素

canvas要素は、Webページにプログラムによる描画領域を確保するための要素です。前回の繰り返しになりますが、次のように記述します。この記述では200×200の描画領域を確保しています。タグで囲まれた「四角形を描画する領域」は、もしもcanvas要素をサポートしていないブラウザでこのページが表示された場合に代替として表示される文字列です。最新のFirefoxやSafariなどのブラウザを利用している限りは、このような文字列が表示されることはありません。

	<canvas id="canvas" width="200" height="200">四角形を描画する領域</canvas>

キャンバス要素をHTML文書の中に挿入するだけでは、単にページに空間が空くだけになります。この文章の直後に空間が空いていると思いますが、これは上記の記述をこのページに挿入しているためです。実際にcanvas要素を活用するためには、JavaScriptによって描画をすることが必要です。次章以降ではJavaScriptによる描画方法について説明します。

canvasの描画領域

なお、通常のグラフでは、X軸は左から右、Y軸は下から上に向かって数字が増えていきますが、canvas要素で描画をする場合にはY軸は上から下に数字が大きくなります。座標を計算する場合はこの点に注意してください。

canvasの座標系

canvasの座標系

JavaScriptによる描画の基本

canvas要素で確保した描画空間にJavaScriptを使って描画をするためには、canvas要素をプログラムの中で取得し、そこから更に描画コンテクストを取得する必要があります。

	var canvas = document.getElementById( "canvas" );
	var ctx = canvas.getContext( "2d" );

上記はcanvas要素の紹介で出てきた四角を描画するプログラムの一部分です。1行目でcanvas要素を取得しています。2行目は新しく出てきた記述です。この行で二次元描画用のコンテクストを取得しています。コンテクストというのは解りにくいかもしれませんが、描画を行うにあたり、どのような座標を使うのか、四角や円などどのような図形を指定することが出来るのかなどを定義したものです。つまり、ここでは「'2d'」と指定することによって、二次元描画コンテクストを取得しています。二次元があるということは「'3d'」と書くと三次元描画もできるのかと思いますが、残念ながら現在のところ唯一使える描画コンテクストは「'2d'」のみです。将来的には三次元描画コンテクストも使えるようになるかも知れません。

キャンバスの描画コンテクストを取得すると、そこに存在している幾つかのプロパティを変更することができます。例えば、線の色や太さなどがそうです。JavaScriptで線の太さを変更するためには、描画コンテクストctxを取得した後に(変数名は「ctx」でなくとも問題ありませんが、ここでは便宜上、上記の続きでとみなして「ctx」を使います)、lineWidthプロパティを変更します。また、線の色を変更するためにはstrokeStyleプロパティを変更します。更に、塗りつぶしを行う場合の色は、fillStyleプロパティを変更することによって指定が可能です。

次のプログラムを見てください。7行目から9行目までで線の太さ、色、塗りつぶしの色を指定しています。また、11行目と12行目でそれぞれ、塗りつぶした四角を描画し、四角の枠を描いています。

<canvas id="canvas" width="200" height="200">四角形を描画する領域</canvas>
 
<script>
	var canvas = document.getElementById('canvas');
	var ctx = canvas.getContext('2d');

	ctx.lineWidth = 10.0;
	ctx.strokeStyle = "black";
	ctx.fillStyle = "lightgrey";

	ctx.fillRect(50, 50, 100, 100);
	ctx.strokeRect(50, 50, 100, 100);
</script>

JavaScriptによる描画の基本(パス)

また、描画においては、より複雑な図形を描画するためにパスという概念があります。これは、いろいろな描画メソッドを組み合わせて図形を描く時に使われるものです。例えば、五角形を描画するメソッドは二次元描画コンテクストには存在しませんが、次のようにすることによって五角形を描くことができます。ここで、beginPath()メソッドからclosePath()メソッドまでがパスを定義している部分になります。その中でmoveTo()メソッド(ペン先を上げた状態で座標に動かす)や、lineTo()メソッド(ペン先を下ろした状態で指定した座標に動かす。つまり、現在ペン先がある所から指定した座標まで直線を引く。)を使って五角形を描き、その上でfill()メソッドによって塗りつぶし、また、stroke()メソッドによって直線を引いた部分に色をつけています。fill()メソッドやstroke()メソッドによって色をつけるまでは、実際には目に見える形にはならないことに気をつけてください。

<canvas id="canvas" width="200" height="200">五角形を描画する領域</canvas>
 
<script>
	var canvas = document.getElementById('canvas');
	var ctx = canvas.getContext('2d');

	x = 100;  // 中心点のX座標
	y = 100;  // 中心点のY座標
	r = 80;   // 半径

	ctx.lineWidth = 3.0;
	ctx.strokeStyle = "black";
	ctx.fillStyle = "lightgrey";

	ctx.beginPath();
	ctx.moveTo( x,  y - r );
	ctx.lineTo(x + 0.8 * r, y - 0.5 *r);
	ctx.lineTo(x + 0.5 * r, y + 0.8 * r);
	ctx.lineTo(x - 0.5 * r, y + 0.8 * r);
	ctx.lineTo(x - 0.8 * r, y - 0.5 *r);
	ctx.fill();
	ctx.stroke();
	ctx.closePath();
</script>

繰返しとキャンバス描画

繰返しについては、while文やfor文などが存在することは既に説明しました。しかし、whileループやforループを単独で使うだけでは、機能として不足することもあります。先ほどのcanvas要素を使って、次のようなチェッカーフラグの様な模様を描くことを考えてみましょう。

四角形を描画する領域

このような模様を描くためには、もちろん根気づよく次のようにプログラミングする方法もあります。

<canvas id="canvas" width="200" height="200">四角形を描画する領域</canvas>
 
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

ctx.fillRect(  0,   0, 20, 20);
ctx.fillRect( 40,   0, 20, 20);
ctx.fillRect( 80,   0, 20, 20);
ctx.fillRect(120,   0, 20, 20);
ctx.fillRect(160,   0, 20, 20);

ctx.fillRect( 20,  20, 20, 20);
ctx.fillRect( 60,  20, 20, 20);
ctx.fillRect(100,  20, 20, 20);
ctx.fillRect(140,  20, 20, 20);
ctx.fillRect(180,  20, 20, 20);

ctx.fillRect(  0,  40, 20, 20);
ctx.fillRect( 40,  40, 20, 20);
ctx.fillRect( 80,  40, 20, 20);
ctx.fillRect(120,  40, 20, 20);
ctx.fillRect(160,  40, 20, 20);

...
</script>

しかし、これでは人間が頑張って、コンピュータに楽をさせているようなものです。もう少し人間が楽になる方法は無いでしょうか? そうです。繰り返しを使えばもう少し楽ができそうです。次のプログラムを見てください。

<canvas id="canvas" width="200" height="200">四角形を描画する領域</canvas>
 
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var y;
for (y=0; i<200; y = y + 20) {
  if (y % 40 == 0) {
    ctx.fillRect(  0, y, 20, 20);
    ctx.fillRect( 40, y, 20, 20);
    ctx.fillRect( 80, y, 20, 20);
    ctx.fillRect(120, y, 20, 20);
    ctx.fillRect(160, y, 20, 20);
  } else {
    ctx.fillRect( 20, y, 20, 20);
    ctx.fillRect( 60, y, 20, 20);
    ctx.fillRect(100, y, 20, 20);
    ctx.fillRect(140, y, 20, 20);
    ctx.fillRect(180, y, 20, 20);
  }
}
</script>

しかし、これでも同じことを何度も書かなければなりませんね。そこで登場するのが繰返しのネスト(多重化)です。ループの中にもう一つループを追加することによってもう少し簡単に書くことができます。

<canvas id="canvas" width="200" height="200">四角形を描画する領域</canvas>
 
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var x, y;
for (y=0; y<200; y+=20) {
  for (x=0; x<200; x+=20) {
    if ((x+y)%40 == 0)
      ctx.fillRect(x, y, 20, 20);
  }
}
</script>

この例では、変数yによって制御されるループの中に、変数xによって制御されるループが入っています。これが二重ループです。次に二重ループのフローチャートを示します。一見複雑ですが、流れを一つ一つ確認していけば理解できると思います。

二重ループのフローチャート

二重ループのフローチャート

☆練習問題 7-2

  1. 次のような図形を描画しなさい。
  2. 四角形を描画する領域

描画のためのメソッド

それでは、実際の描画方法についてみていきましょう。以下に描画に使われる代表的なメソッドをまとめておきます。それ以外の細かな仕様は、以下のサイトを参照して下さい。

 Canvasリファレンス
 Canvasチュートリアル

最初はパス関係のメソッドです。キャンバスのコンテキストのオブジェクトは省略し、メソッド名だけを表示しています。

メソッド 説明
beginPath( ) 現在のパスをリセットして、新たにパスを書き始める。
stroke( ) これまでのパスを線で描画する。
fill( ) これまでのパスを塗り潰す。
moveTo(x, y) ペン先を上げた状態で、(x, y)で指定された座標にペン先を動かす。
lineTo(x, y) 現在ペン先がある位置から、(x, y)で指定された座標まで直線を引く。
bezierCurveTo(x0, y0, x1, y1, x2, y2) 3次ベジェ曲線を描く。現在の座標を始点、(x2, y2)を終点、(x0, y0)を始点に対する制御点、(x1, y1)を終点に対する制御点とするベジェ曲線を描く。
quadraticCurveTo(x0, y0, x1, y1) 2次ベジェ曲線を描く。現在の座標を始点、(x1, y1)を終点、(x0, y0)を中間制御点とするベジェ曲線を描く。
rect(x, y, w, h) (x, y)で指定された座標を起点として、幅w、高さhの四角形を描画する。
arc(x, y, r, sAng, eAng, clockwise) (x, y)を中心とするsAngで指定された角度からeAngで指定された角度までの半径rの弧を描く。また、clockwiseがtrueの場合は反時計回り、falseの場合は時計回りとなる。

quadraticCurveToとbezierCurverToは、次の図を参考にしてみて下さい。

以下のメソッドは、lineTo()などと異なり、stroke()メソッドやfill()メソッドを用いなくとも描画されるものです。

メソッド 説明
fillRect(x, y, w, h) (x, y)で指定された座標を起点として、幅w、高さhの塗りつぶされた枠の無い四角形を描画する。
clearRect(x, y, w, h) (x, y)で指定された座標を起点として、幅w、高さhの背景色で塗りつぶされた枠の無い四角形を描画する。
strokeRect(x, y, w, h) (x, y)で指定された座標を起点として、幅w、高さhの塗りつぶされていない枠のみの四角形を描画する。
fillText(text, x, y) textで指定された文字列を座標(x,y)に書く。
strokeText(text, x, y) textで指定された文字列の輪郭を座標(x,y)に書く。

描画のためのプロパティ

プロパティは、代入することで値を設定できます。また、参照することで値を持ってくることができます。

プロパティ 説明
strokeStyle 輪郭を描くときの色やスタイルを持ちます。色は色名か#rrggbb、またはrgb(赤, 緑, 青)という文字列で指定します。グラデーションを指定するときは、グラデーションを保持するオブジェクト(変数名)を指定します。
fillStyle 塗り潰すときの色やスタイルを持ちます。色は色名か#rrggbb、またはrgb(赤, 緑, 青)という文字列で指定します。グラデーションを指定するときは、グラデーションを保持するオブジェクト(変数名)を指定します。
globalAlpha 描画するときの透明度を持ちます。0.0 (完全に透明)から 1.0 (透明度なし)までの範囲内の値になっています。
lineWidth 輪郭の幅(ピクセル)を持ちます。初期値は1.0です。
lineCap 輪郭の端点の形状を持ちます。取り得る値は、butt, round, square の3つです。文字列で指定します。 初期値はbuttです。 buttは、端点に対してフラットな形状、roundは半円になります。squareは、線の幅の半分となる矩形が追加されます。
lineJoin 輪郭の折れ線を接続するときの形状を持ちます。取り得る値は、bevel, round, miterの3つです。文字列で指定します。初期値はmiterです
shadowBlur 影のぼかしの値(ピクセルの大きさ)を持ちます。初期値は0です。
shadowColor 影のカラー(色指定)を持ちます。
shadowOffsetX 影の水平方向の差分値を持ちます。初期値は0です。
shadowOffsetY 影の垂直方向の差分値を持ちます。初期値は0です。

上記の色指定ですが、#rrggbbという指定の場合、rr, gg, bbは10進数で0〜255までを表す16進数の2桁を数字です。それぞれ、赤、緑、青の色の成分の大きさを表します。これらの色成分は加色混合されます。次のような感じです。

#ffffff…白    #ff0000…赤    #00ff00…緑     #0000ff…青
#000000…黒  #ffff00…黄     #00ffff…シアン   #ff00ff…マゼンタ
#808080…灰

色をrgbまたは、rgbaで指定する場合は、次のような文字列になります。

rgb( 赤色成分, 緑色成分, 青色成分 )
rgba( 赤色成分, 緑色成分, 青色成分, 透明度 )

rgbのそれぞれの成分の値は、0〜255までの整数で、255が一番強くなります。上記の指定と同様に加法混色されます。透明度は、0が完全な透明で、1が完全な不透明になります。0と1の間の小数で指定します。

色を色名で指定する場合、次のような色が使えるようです。なお、灰色を示す「gray」は、英国綴りで「grey」でも良いようです。

JavaScriptで指定できる色の名前

たとえば、以下の指定は塗りの色をすべてオレンジ色に指定しています。

	ctx.fillStyle = "orange";
	ctx.fillStyle = "#FFA500";
	ctx.fillStyle = "rgb(255,165,0)";
	ctx.fillStyle = "rgba(255,165,0,1)";
	

次の例は、カラーテーブルを黒〜赤〜青〜マゼンタの範囲で作成しているものです。 左から右方向に赤色成分が強くなり、上から下方向に青色成分が強くなります。

	for ( var y = 0; y < 16; y=y+1 ) {
		for ( var x = 0; x < 16; x=x+1 ) {
			ctx.fillStyle = "rgb( " + x*16 + ", 0, " + y*16 + ")";  // xとyを赤色、青色成分値としても利用
			ctx.fillRect( x*20, y*20, 19, 19 );
		}
	}  
	

lineCapの形状は、下記のようになります。左から、butt, round, squareを表しています。

lineCapプロパティのサンプル

lineJoinの形状は、下記のようになります。

lineJoinプロパティのサンプル

以下のスクリプトは、線幅を15ピクセルにして、roundとbevelを指定して折れ線を描いたものです。

	ctx.lineCap = "round";
	ctx.lineJoin = "bevel";
	ctx.lineWidth = 15;
	ctx.beginPath( );
	ctx.moveTo( 50, 20 );
	for ( var i=0; i < 10; i++ ) {
		ctx.lineTo( 100+i*50, (i % 2 ==0) ? 80: 20 );
	}
	ctx.stroke( );
	

結果は次のような感じになります。

ライン属性を描画する領域

シャドウ関係のプロパティを設定した例です。

	ctx.lineWidth = 10;
	ctx.strokeStyle="pink";
	ctx.shadowBlur = 20;
	ctx.shadowColor="blue";
	ctx.shadowOffsetX = 5;
	ctx.shadowOffsetY = 5;
	ctx.strokeRect( 50, 50, 100, 100 );
	

結果は次のような感じになります。

シャドウ属性を描画する領域

テキスト関係のプロパティは次のようなものがあります。

プロパティ 説明
font CSSと同様なフォントの属性の文字列を持ちます。
textAlign テキストの水平方向の揃えを持ちます。その値は、start, end, left, right, centerのいずれかです。 初期値は、startです。値は文字列で指定します。
textBaseline テキストのベースラインのスタイルを持ちます。その値は、top, hanging, middle, alphabetic, ideographic, bottomのいずれかです。初期値は、alphabeticです。値は文字列で指定します。
	// 12px 'Times New Roman
	ctx.font = "12px 'Times New Roman'";
	ctx.fillText("Times New Roman", 10, 15);
	
	// 18px 'Baskerville'
	ctx.font = "18px 'Baskerville'";
	ctx.fillText("Baskerville", 10, 36);
	
	// bold 22px 'ヒラギノ角ゴ Pro W3'
	ctx.font = "bold 22px 'ヒラギノ角ゴ Pro W3'";
	ctx.fillText("ヒラギノ角ゴ Pro W3", 10, 65);
	
	// italic bold 26px 'Optima' */
	ctx.font = "italic bold 26px 'Optima'";
	ctx.fillText("Optima", 10, 95);
	

このように指定すると、こんな感じです。

フォントを描画する領域

テキストの揃え(textAlign)は次の図を参考にして下さい。

textAlignプロパティのサンプル

テキストのベースラインの揃え(textBaseline)は次の図を参考にして下さい。

textBaselineプロパティのサンプル

グラデーション

円形グラデーションと線形グラデーションが使えます。

メソッド 説明
createLinearGradient(x0, y0, x1, y1) 線形のグラデーション領域を作って返す。座標x0, y0からx1, y1に対して作られる。
createRadialGradient(x0, y0, r0, x1, y1, r1) 円形のグラデーション領域を作って返す。最初の円(中心座標x0, y0,半径r0)から 最後の円(中心座標x1, y1,半径r1)に向かってグラデーションが作られる。
addColorStop(offset, color) グラデーション領域に、色指定を追加する。offsetは、0以上1以下の数字で、グラデーション領域の 相対的な位置を示す。colorは、色指定をする文字列。

以下にそれぞれのグラデーションの例を記述してみます。

		ctx.beginPath( );
		// 線形グラデーションの指定
		var grad1  = ctx.createLinearGradient( 0, 0, 200, 200 );
		// グラデーション始点と終点の色を指定
		grad1.addColorStop( 0, "rgb(240, 0, 0 )" );    // ほぼ赤
		grad1.addColorStop( 1, "rgb(0, 0, 255)" );  // 青
		// グラデーションをfillStyleプロパティに指定
		ctx.fillStyle = grad1;
		// 正方形を描画
		ctx.rect( 0, 0, 200, 200 );
		ctx.fill( );

		ctx.beginPath( );
		// 円形グラデーションの指定
		var grad2  = ctx.createRadialGradient( 400, 200, 50, 400, 200, 150 );
		// グラデーションの各地点の色を指定
		grad2.addColorStop( 0, "rgb(0, 255, 0 )" );    // 緑
		grad2.addColorStop( 0.5, "rgb(255, 255, 0 )" );    // 黄
		grad2.addColorStop( 1, "rgb(255, 0, 0 )" );  // 赤
		// グラデーションをfillStyleプロパティに指定
		ctx.fillStyle = grad2;
		// 正方形を描画
		ctx.rect( 300, 0, 350, 350 );
		ctx.fill( );

	

結果はこんな感じになります。

座標軸変換のためのメソッド

座標軸自体を変換して、描画図形全体を回転させたり、縮小拡大、移動することができます。

メソッド 説明
translate( dx, dy ) 座標軸の中心をx方向にdx、y方向にdy移動します。
rotate( angle ) 座標軸を時計方向にangle度だけ回転させます。なお、angleは、ラジアン角度(180度がπになっている)で 指定する必要があります。
scale( sx, sy ) 座標の縮尺に、x方向にsx、y方向にsyを掛けます。sx, sy共に、0よりも大きく1.0以下の場合は、 縮小になります。1.0よりも大きい場合は拡大になります。また、マイナスの場合は鏡面変換になります。
save( ) 座標軸や描画の状態を保存しておきます。保存はスタック(後入れ先だし)構造になっています。
restore( ) 保存しておいた座標軸や描画の状態を復帰します。復帰される状態は、スタックの最後に追加されたものが 取り出されます。

たとえば、次の例は四角形を20度回転させてから再度描画しています。

	ctx.save( );
	// 座標軸の中心を移動する
	ctx.translate( 100, 50 );
	// 元の四角形を赤色で描く
	ctx.strokeStyle = "red";
	ctx.strokeRect( -60, -25, 120, 50 );
	// 座標軸を20°回転させる
	ctx.rotate( 20 * 3.141592 / 180 );
	// 四角形を青色で描く
	ctx.strokeStyle = "blue";
	ctx.strokeRect(-60, -25, 120, 50 );
	ctx.restore( );
	

結果はこんな感じになります。

☆練習問題7-3

  1. プロパティを変えて、描画してみなさい。
  2. arcや、quadraticCurveToなどを用いて洋上の月を描いてみなさい。ファイル名は、LunaOcean.html