
今回は複数エレメントを扱います。それぞれのエレメントを動かす関数をそれぞれ用意して実行させるということも可能ですが、同じ処理を複数の関数で実行させるのは無駄が多くなり、また汎用性が低くなりますので、同じ処理は同じ関数で処理させるようにします。このため引数をもつ関数を使用します。
引数を持つ関数については講座13で既に解説済みですが、復習しておきましょう。
functionステートメント:新しい関数を宣言します。
引数:引数1〜引数n:省略可能です。関数に渡される引数を指定します。複数の引数を指定する場合は、カンマで区切ります。
処理:省略可能です。1 つ以上の JavaScriptかJScript ステートメントを指定します。
解説:function ステートメントは、関数を宣言するときに使用します。処理ステートメントに記述したコードは、スクリプトで他の場所からこの関数が呼び出されるまでは実行されません。使用例:次のコードは、function ステートメントの使用例です。 底辺と高さから長方形の面積をを求める。
function myfunction(arg1, arg2){
var r;
r = arg1*arg2;
return(r);
}
メモ 関数を呼び出すときは、必須の引数とかっこを記述してください。かっこを付けずに関数を呼び出すと、関数の結果ではなく、関数のテキストが返されます。
引数(ひきすう)は、関数に値を渡す際に使用します。関数を呼び出す側からは、与える値を引数同様にカンマで区切って記述します。
上記の場合は、myfunction(5,4)とすれば、arg1に5が渡され、arg2に4が渡されます。return(r)により、呼び出した側に関数内で処理したrの値が返されます。
rのことを関数の戻り値(結果)といい、これを受け取るためには呼び出し側が受け取る器を用意しておかなければなりません。
上記の場合なら呼び出す際に次のようにします。
var menseki=myfunction(5,4)
mensekiという変数を用意し、関数を呼び出します。関数は引数として呼び出し側から値を受け取り処理します。戻り値(返り値とも)rにより呼び出し側mensekiにrの値が代入されます。
var menseki=myfunction(5,4)ですと、mensekiは20になることがわかります。
前回の関数部分は次のようになっていました。
function
en(){
img1.style.pixelLeft=Math.cos(deg*Math.PI/180)*hn+cx
img1.style.pixelTop=Math.sin(deg*Math.PI/180)*hn+cy
deg=deg+dd//deg+=ddでもOK
setTimeout("en()",100)
}
この関数ではimg1エレメントだけに働きかけていますね。またdegはimg1の角度用に用意していました。hnとcx,cyですが、複数エレメントに当てはめるため、複数用意するとそれぞれ半径や中心を変えて円運動できますが、ここでは半径と中心はどのエレメントに対しても同じにして同心円上に動かしたいと思います。そこでhnとcx,cyはこのまま利用します。
さて、エレメントの角度ですが、配列オブジェクト(配列変数)を使用します。img1ではdeg[1]を、img2ではdeg[2]を・・・という具合です。
さて根本的問題です。円運動をどのように行うかというアイディアが決まっていませんね。そこで次のようにしたいと思います。
@エレメントの数を変数に予め入れておく。
Aエレメントは一つ目は角度0の位置から回転を始める。二つ目以降は次の図のようにしたい。

円周上に等間隔に複数の画像を配置して、同じ角速度で同じ円周上を回転させたいのです。このためにはそれぞれの初期角度を等間隔になるように設定して、同じ角度の増加量で移動後の角度を計算し、その角度から移動後のLEFTとTOPを計算すればできそうですね。
エレメントの数をesとすると、esが1の時、エレメントの数は1個なので、0度の位置にひとつだけ配置するようにします。
esが2の時は、二つのエレメントを0度と180度の位置に配置します。
esが3の時は三つのエレメントを0度と120度と240度の位置に配置します。
esが4の時は四つのエレメントを0度と90度と180度と270度の位置に配置します。
esがnの時は?

n個のエレメントを0度と360/n*1と360/n*2と・・・360/n*(n-1)の位置に配置すると考えられますね。
これをスクリプトでできないか考えます。
var es=3//エレメントの数を入れる
var deg=new Array()//配列オブジェクト。それぞれの角度計算用です。
for(i=0;i<es;i++){
deg[i+1]=360/es*i
}
一つ目のエレメントに関する番号を1からつけることにします。
番号を0からつけても構いませんが、その場合は
var es=3//エレメントの数を入れる
var
deg=new Array()//配列オブジェクト。それぞれの角度計算用です。
for(i=0;i<es;i++){
deg[i]=360/es*(i-1)
}
としてくださいね。
iを1から始める場合で番号を1からつける場合は
var es=3//エレメントの数を入れる
var
deg=new Array()//配列オブジェクト。それぞれの角度計算用です。
for(i=1;i<=es;i++){
deg[i]=360/es*(i-1)
}
としてくださいね。
iが0時、iが2の時・・・と実際にiがとりうる値を当てはめてみるとわかるかと思います。
これで初期角度は設定できました。エレメントに対応する番号は1からつけていることに注意してください。一番目のエレメントの角度はdeg[1]で、このエレメントのidはimg1というように統一したいわけです。二番目のエレメントのidはimg2とつけます。この角度は勿論deg[2]です。n番目のエレメントはimgnで、角度はdeg[n]です。
さて関数内でnという変数を使用してimg1やimg2というオブジェクトを指定したい場合ですが、これが結構ややこしいのです。
"img"+nという文字列連結ですと、確かに文字列としてimg1やimg2という風にnの値によって変更ができますが、これはオブジェクトとして認識してくれません。
"img"+n+".style.pixelLeft"=Math.cos(deg[n]*Math.PI/180)*hn+cx
のようにはできないのです。かといって
"img"+n+".style.pixelLeft=Math.cos(deg[1]*Math.PI/180)*hn+cx"
ですと、これは文字列の連結ですが代入先あるいは式として成り立ちません(式として成り立たなくてもスクリプトエラーにはならず、この場合コメントのように扱われます)。
例)
"こんにちは"
文字列だけが評価され指揮として成り立たないので、エラーにはなりませんが、意味を成さないのでこの性質を利用してコメントを書くことも可能です。
さて文字列をスクリプトの式として評価されるものがJavaScriptに存在すればいいのですが・・・。
はい、これが実は用意されているのですね。文字列を数値に変換、あるいはスクリプト式に評価するメソッドが存在します。
MSDNを見てみましょう。
例)
var
n=5
eval("img"+n+".src='hogehoge.gif'")
evalの中では"img5.src='hogehoge.gif'"という文字列になりますが、これはスクリプト文字列としてevalメソッドで評価されるため、img5.src='hogehoge.gif'と同等になります。
この講座で文字列に数値を加算させると、文字列の連結になると書きました。
"5"+3は"53"という文字列になり、"5"*3は15という数値になります。文字列を数値で乗算することはできないため、スクリプトでは文字列が数値として扱える場合は数値計算になります。
さて5+3を8にしたい場合は強制的に"5"を数値として扱うよう命令しなければなりません。それがeval()メソッドです。
eval("5")+3とすることで8という結果を得ることができます。
このeval()メソッドで変数nに対してエレメントを評価させましょう。
eval("img"+n).style.pixelLeft=Math.cos(deg[n]*Math.PI/180)*hn+cx-eval("img"+n).clientWidth/2
eval("img"+n).style.pixelTop=Math.sin(deg[n]*Math.PI/180)*hn+cy-eval("img"+n).clientHeight/2
あるいは
eval("img"+n+".style.pixelLeft=Math.cos(deg["+n+"]*Math.PI/180)*hn+cx-img"+n+".clientWidth/2")
eval("img"+n+".style.pixelTop=Math.sin(deg["+n+"]*Math.PI/180)*hn+cy-img"+n+".clientHeight/2")
としてもできそうですが、これはテストしてみればわかります。前者はimgnの部分だけをスクリプト文字列として扱う構想で、後者は全体をスクリプト文字列として扱う構想です。実際にはどちらでもOKです。
さあ前回のソースと照らしまとめてみましょう。三つの場合です。
<HTML>
<head>
<meta http-equiv="content-type"
content="text/html; charset=shift_jis">
<META http-equiv="Content-Style-Type"
content="text/css">
<title></title>
</head>
<body onload="setTimeout('en()',3000)"
style="overflow:auto; margin:0px; padding:0px">
<img
id="img1" src="octopus.gif" style="position:absolute;
left:-1000px; top:0px">
<img
id="img2" src="b_penguin_blue_1.gif" style="position:absolute;
left:-1000px; top:0px">
<img
id="img3" src="a_whale_blue.gif" style="position:absolute;
left:-1000px; top:0px">
<script>
var
es=3//エレメントの数を入れる
var deg=new Array()//配列オブジェクト。それぞれの角度計算用です。
for(i=1;i<=es;i++){
deg[i]=360/es*(i-1)
}
var
dd=5//角度変化量(プラスで右回り、マイナスで左回り)
var
cx=300//中心X座標
var cy=200//中心Y座標
var hn=150//半径
function
en(n){
eval("img"+n).style.pixelLeft=Math.cos(deg[n]*Math.PI/180)*hn+cx-eval("img"+n).clientWidth/2
eval("img"+n).style.pixelTop=Math.sin(deg[n]*Math.PI/180)*hn+cy-eval("img"+n).clientHeight/2
deg[n]=deg[n]+dd//deg[n]+=ddでもOK
setTimeout("en("+n+")",100)
}
</script>
</body>
</html>
変数部分は文字列ではないので、""で囲まずに変数の値が計算されて文字列に連結されるようにしています。
"en("+n+")"のところや"img"+nの部分、あるいはdeg["+n+"]の部分に注意してください。文字列の連結ですが、連結後が"en(n)"や"imgn"や"deg[n]などにならないように。引数nに対し、nの内容を連結したいわけで、文字列"n"を連結するのではありません。
でこのひな形は動きません。関数実行はonload="setTimeout('en()',3000)"によるものですが、引数に実際の値を与えなければなりません。しかしonload="setTimeout('en(1)',3000)"の場合ですとエレメント番号1しか開始されません。1〜n個のエレメントを動かすためには数分のen()関数実行が必要になります。
そこで開始される関数を別に用意しその関数によって1〜n個のen()関数実行をさせましょう。
enstart()という関数定義します。このスタート関数で、for〜構文で1からnまでen()関数を実行させます。
function enstart(){
for(i=1;i<=es;i++){
en(i)
}
[ソース]
<HTML>
<head>
<meta http-equiv="content-type"
content="text/html; charset=shift_jis">
<META http-equiv="Content-Style-Type"
content="text/css">
<title></title>
</head>
<body onload="setTimeout('enstart()',3000)"
style="overflow:auto; margin:0px; padding:0px">
<img
id="img1" src="octopus.gif" style="position:absolute;
left:-1000px; top:0px">
<img
id="img2" src="b_penguin_blue_1.gif" style="position:absolute;
left:-1000px; top:0px">
<img
id="img3" src="a_whale_blue.gif" style="position:absolute;
left:-1000px; top:0px">
<script>
var
es=3//エレメントの数を入れる
var deg=new Array()//配列オブジェクト。それぞれの角度計算用です。
for(i=1;i<=es;i++){
deg[i]=360/es*(i-1)
}
var
dd=5//角度変化量(プラスで右回り、マイナスで左回り)
var
cx=300//中心X座標
var cy=200//中心Y座標
var hn=150//半径
function
en(n){
eval("img"+n+".style.pixelLeft")=Math.cos(deg[n]*Math.PI/180)*hn+cx-eval("img"+n+".clientWidth/2")
eval("img"+n+".style.pixelTop")=Math.sin(deg*Math.PI/180)*hn+cy-eval("img"+n+".clientHeight/2")
deg[n]=deg[n]+dd//deg[n]+=ddでもOK
setTimeout("en("+n+")",10)
}
function
enstart(){
for(i=1;i<=es;i++){
en(i)
}
}
</script>
</body>
</html>
eval()の使い方を変えたもの三つ等間隔円運動2![]()
eval("img"+n+".style.pixelLeft=Math.cos(deg["+n+"]*Math.PI/180)*hn+cx-img"+n+".clientWidth/2")
eval("img"+n+".style.pixelTop=Math.sin(deg["+n+"]*Math.PI/180)*hn+cy-img"+n+".clientHeight/2")
そうそうa/b*cはa/bにcを乗算します。b*cでaを割るのではありません(/と*は優先順位が同じため、記述順に計算されます)。b*cでaを割るには、a/(b*c)と括弧を使用して演算の優先順位を変えてください。()は/や*よりも優先順位が高いです。
10/5*2・・・4になります。10/(5*2)・・・1になります。
さてdeg[n]=deg[n]+ddですが、これは計算が繰り返されるごとにdeg[n]の絶対値は大きくなっていきます。どこまでも大きくなっていくわけですが、これで問題はないのかという疑問がわきますね。
一応最大値(整数・負数)は上限は ±1.7976931348623157x10308らしく、これはひな形ではほとんど無限大に近いのではないかと思います。しかしどうしても気になるようでしたら、次のようにすると-360〜360までの数値にすることができます。
deg[n]=(deg[n]+dd)%360・・・360で割った時の剰余を求めます。例えば375度は15度に同じです。721度は1度に同じです。
次はエレメントを複数設置、使用画像を複数利用、エレメントの数を設定すると使用画像を順番に入れていく方法を行います。ひな形の汎用度が高まります。