JavaScript
12. 関数で処理をまとめる
プログラムでは、良く利用する複数の命令を独自の処理としてまとめるためなどに、関数というものが定義できます。
関数の利用にはプログラムを簡素化できる、機能や意味の単位に分割し分かりやすくするなどのメリットがあります。
以下では関数の使い方を説明しつつ、描画を行ってみます。
独自に四角形を描く処理を定義する
CanvasにはstrokeRect()、fillRect()という矩形を描く命令が予め用意されていますが、正方形か長方形(4つの角がすべて直角の図形)しか描くことができません。
そこで、四角形を描く処理を独自に考えてみます。
window.addEventListener( "load", loadFunc, false ); function loadFunc() { var canvas = document.getElementById( "stage" ); var ctx = canvas.getContext( "2d" ); ctx.fillStyle = "#99ccff"; ctx.beginPath(); ctx.moveTo( 0, 0 ); ctx.lineTo( 100, 0 ); ctx.lineTo( 100, 100 ); ctx.lineTo( 0, 100 ); ctx.closePath(); ctx.fill(); }
この四角形を書く処理は、10〜15行まとめて「四角形のパスを描く」という1つの処理を担います。
関数によって、このような場合の複数行にまとめて1つの処理としての名前を付ける、ということができます。
書式は以下のようになります。
function 関数名( 引数 ) {
関数として行う処理;
}
functionは変数を定義するときのvarのようなもので、varの場合は続く文字列が変数名だったのに対し、functionは続く文字列を関数名として定義します。
関数名も変数名同様、半角英数字と_(アンダーバー)のみが使用でき先頭は英字のみになります。
関数名に続く{から}終わりまでの間に、関数として行う処理を書きます。
window.addEventListener( "load", loadFunc, false ); function loadFunc() { var canvas = document.getElementById( "stage" ); var ctx = canvas.getContext( "2d" ); ctx.fillStyle = "#99ccff"; drawQuadrangle( ctx ); ctx.fill(); } function drawQuadrangle( ctx ) { ctx.beginPath(); ctx.moveTo( 0, 0 ); ctx.lineTo( 100, 0 ); ctx.lineTo( 100, 100 ); ctx.lineTo( 0, 100 ); ctx.closePath(); }
17〜22行目は前記の四角形を描く処理に同じですが、16行目と23行目の間に書くことによって、17〜22行目をdrawQuadrangleという名前の関数として1まとまりの処理として定義したことになります。
この書き方は、3〜14行目と同じであり、今までプログラムを書いてきたloadFuncというのも、実は関数(1行目でファイルが読み込まれた時に実行するように指定された関数)を定義し、その中でプログラムをしていました。
drawQuadrangle()の()の中ですが、ctxと書かれています。loadFunc()とdrawQuadrangle()は別個に書かれており、ctxはloadFunc()の中である6行目で定義されているため、何もしないとloadFunc()の外に書かれたdrawQuadrangle()の{}の中(17〜22行目)でctxを使うことができません。ctxが使えないと、biginPath()やmoveTo()、lineTo()などの命令が使えないので、このような場合には、drawQuadrangle()の()の中(関数を定義する際の()の中)に、drawQuadrangle()の{}の中で使いたい変数を書いてあげます。そうするとdrawQuadrangle()の{}の中でもctxを使うことができます。
なおこれは、対応さえ取れていれば、ctxと同じ文字列にしなくても構いません(むしろ普通は変えます)。以下のように書いてもこの場合は同じ意味になります。
window.addEventListener( "load", loadFunc, false ); function loadFunc() { var canvas = document.getElementById( "stage" ); var ctx = canvas.getContext( "2d" ); ctx.fillStyle = "#99ccff"; drawQuadrangle( ctx ); ctx.fill(); } function drawQuadrangle( argCtx ) { argCtx.beginPath(); argCtx.moveTo( 0, 0 ); argCtx.lineTo( 100, 0 ); argCtx.lineTo( 100, 100 ); argCtx.lineTo( 0, 100 ); argCtx.closePath(); }
10行目、drawQuadrangle( ctx )が実行されると、その処理は16行目移行に移り、ctxはargCtxに引き継がれます。関数に渡される数を引数と言います。drawQuadrangle()の{}の中ではctxの代わりにargCtxを使うことができます。
ここまでの例では分かりづらいかと思いますが、数値などを引き渡す場合の方が分かりやすかもしれません。
例えば現状では、同じ場所に同じ大きさの四角形しか描くことができませんが、drawQuadrangle()を使うたびに、座標を指定できるようにすることも可能です。
window.addEventListener( "load", loadFunc, false ); function loadFunc() { var canvas = document.getElementById( "stage" ); var ctx = canvas.getContext( "2d" ); ctx.fillStyle = "#99ccff"; ctx.strokeStyle = "#99ccff"; drawQuadrangle( ctx, 0, 100 ); ctx.fill(); drawQuadrangle( ctx, 100, 100 ); ctx.stroke(); } function drawQuadrangle( argCtx, firstPoint, size ) { var tmpPoint = firstPoint + size; argCtx.beginPath(); argCtx.moveTo( firstPoint, firstPoint ); argCtx.lineTo( tmpPoint, firstPoint ); argCtx.lineTo( tmpPoint, tmpPoint ); argCtx.lineTo( firstPoint, tmpPoint ); argCtx.closePath(); }
19行目の関数定義部分で、関数に渡す引数を,で区切って3つに増やし、()の中をargCtx, firstPoint, sizeとしています。
11行目のdrawQuadrangle()の()内には、ctx, 0, 100と書かれていますが、ctxはargCtxに、0はfirstPointに、100はsizeにそれぞれ引き継がれ、処理が実行されます。
このようにすると11行目、14行目のように、同じ関数を値を変えて実行することができるようになります。
いくつかの関数の定義方法を検討する
用途に応じて、定義方法を使い分けられるようになるとさらに便利です。
例えば中心座標を与えて四角形を描きたいのであれば、以下のようになります。
window.addEventListener( "load", loadFunc, false ); function loadFunc() { var canvas = document.getElementById( "stage" ); var ctx = canvas.getContext( "2d" ); ctx.fillStyle = "#99ccff"; ctx.strokeStyle = "#99ccff"; drawQuadrangle( ctx, 100, 100, 50 ); ctx.fill(); drawQuadrangle( ctx, 250, 250, 100 ); ctx.stroke(); } function drawQuadrangle( argCtx, centerX, centerY, size ) { var drawPointX1 = centerX - size / 2; var drawPointX2 = centerX + size / 2; var drawPointY1 = centerY - size / 2; var drawPointY2 = centerY + size / 2; argCtx.beginPath(); argCtx.moveTo( drawPointX1, drawPointY1 ); argCtx.lineTo( drawPointX2, drawPointY1 ); argCtx.lineTo( drawPointX2, drawPointY2 ); argCtx.lineTo( drawPointX1, drawPointY2 ); argCtx.closePath(); }
19行目、centerXとcenterYという中心を関数で受け取れるようにし、関数の中ではその値を利用して、四角形を描くような処理にしています。
どのような処理を行いたいかに応じて、関数を作れるようになると表現の幅がひろがるでしょう。
window.addEventListener( "load", loadFunc, false ); function loadFunc() { var canvas = document.getElementById( "stage" ); var ctx = canvas.getContext( "2d" ); var repeatNum = 9; var pitch = 50; var quadSize = 50; var pitchWidth = pitch * ( repeatNum - 1 ); var leftX = ( canvas.width - pitchWidth ) / 2; ctx.fillStyle = "hsla( 210, 40%, 50%, 0.4 )"; for( var i = 0; i < repeatNum; i++ ) { for( var j = 0; j < repeatNum; j++ ) { drawQuadrangle( ctx, leftX + j * pitch, leftX + i * pitch, quadSize ); ctx.fill(); } } } function drawQuadrangle( argCtx, centerX, centerY, size ) { var random1 = Math.floor( Math.random() * size ) + 1; var random2 = Math.floor( Math.random() * size ) + 1; var random3 = Math.floor( Math.random() * size ) + 1; var random4 = Math.floor( Math.random() * size ) + 1; argCtx.beginPath(); argCtx.moveTo( centerX - random1, centerY - random1 ); argCtx.lineTo( centerX + random2, centerY - random2 ); argCtx.lineTo( centerX + random3, centerY + random3 ); argCtx.lineTo( centerX - random4, centerY + random4 ); argCtx.closePath(); }