
前回では「フローチャート」について少し触れました。フローチャートはプログラムによる一連の処理過程(この過程をプロセスといいます)の手順を表すものです。頭の中だけで考えるよりも図式化することによりプログラミングしやすくなります。
では、前回のマウス追従において、遅れてついてくるようにしたいと思います。この考えを具体化する時にフローチャートとして図式化しておくと、プログラムを実際に組む時ミスが少なくなります。皆さんも考えをまとめる前に、まずアイディアを出し、それを実現するための過程を図式化してみてください。
まず思いついた考えを簡単にまとめてみましょう。
@マウスの座標を取得、これをzxとzyという変数に代入しておく。
A現在の位置をlxとtyという変数で表すようにする。
Bzxとlxの差分の半分だけlxをzxに近づける。zyとtyの差分の半分だけtyをzyに近づける。
Cもし差分の半分が1ピクセルに満たない場合はlxはzxにする、tyはzyにする。
Dlxとzxが同じである時は実行しない。またtyとzyが同じ場合も実行しない。
E近づけるために行う処理はsetTimeout()で繰り返させる。
ぱっと思い立った感じはこんな感じです。まだアイディア段階で実現できるかどうかはわかりません。このアイディアを更に具体化してまとめて行きます。
@マウス座標をzx,zyに入れる。これにより移動する目標座標が決まります。これはマウスが移動したらzxとzyの値を代入しなおせばいいので簡単です。ひな形を開いた時にマウス移動イベントが起きないとzxとzyは設定されないことになります。zxとzyの値が決定されていないと目標がわからないので、初期値を決めておきましょう。とりあえず左上端である<0,0)にしましょう。とするとvar zx=0;var zy=0と式を記述しておくべきですね。
マウスが移動したら=>bodyエレメント上で移動が起これば、zxとzyにマウス座標を代入するのですから、bodyタグにonmousemoveを記述、マウス座標を代入する関数を用意して実行させるようにしましょう。エラーが起きないように関数は<head>〜</head>内に記述しておきますね。関数名はzahyo()にしましょうか。
<script>
var zx=0
var zy=0
function zahyo(){
zx=event.x
zy=event.y
}
</script>
</head>
<body
onmousemove="zahyo()" style="overflow:auto;
margin:0px; padding:0px">
A現在のmcboxエレメントの位置(leftとtop)をlxとtyに入れておく。これはlxとtyを使用して計算を行い、その結果をlxとtyに代入し直してからその値をleftとtopにすると考えられます。では最初のmcboxの位置をlxとtyに入れるのは?
やはり数値でlxとtyを扱いたいので、leftとtopも数値を返すpixelLeftとpixelTopを使用したいですね。ということで、lxとtyはmcboxエレメントが認識された後にそのleftとtopの値を代入しておきたい。
<div id="mcbox" style="width:100px; height:100px; position:absolute;
top:0px; left:0px; overflow:visible; border:red 1px solid">
<img
id="img1" src="octopus.gif" style="position:absolute">
</div>
<script>
var
lx=mcbox.style.pixelLeft
var ty=mcbox.style.pixelTop
</script>
Bzxとlxの差分の半分だけlxをzxに近づける。zyとtyの差分の半分だけtyをzyに近づける。この部分はそれぞれ関数にしてみましょう。ここでは関数名はそれぞれapprochX()とapprochY()にしました。
さて、差分の半分だけという部分ですが、差分が偶数の場合はいいのですが、奇数の場合は半分という値は少数になってしまいます。座標はピクセル単位で扱っているので小数では都合が悪い。そこで小数部分は切り上げするようにします。半分ずつ近づくということは必ず差分が2か1になる時がきます。2の時は近づける距離は1になり、残り1ピクセル離れていることになります。差分が1の時は近づける距離はやはり1になり、結局マウス座標の位置に一致することができます。
さあここで問題が発生!!差分って必ずしも正数じゃないですよね。zxがlxより小さい時も考えられます。例えば差分が-5だったらその半分は-2.5ですね。これを-3にしたいわけです。差分が5の時は3にしたいんです。ところが切り上げを行う関数は負数の時にも正数の時にも与えられた数値以上の最小の整数になります。2.5なら3になるのですが、-2.5では-2になります。ちなみに切り上げを行う関数はMath.ceil(n)です。数値nを切り上げした整数を得ます。
Math.ceil(n)・・・nの小数点以下を切り上げした値を得る。
さあどうしましょう。もうひとつ紹介すると、切り捨てを行う関数は与えられた値以下の最大の整数を得ます。2.5を切捨てすると2に、-2.5を切捨てすると-3になります。
Math.floor(n)・・・nの小数点以下を切り捨てした値を得る。
差分が正数の時と負数の時で場合分けをしてMath.ceilとMath.floorで使い分けますか?
if(zx-lx>0){
lx=Math.ceil((zx-lx)/2)+lx
}else{
lx=Math.floor((zx-lx)/2)+lx
}
勿論これでもOKです。しかしif〜文を使用しないでできないでしょうか?
Math.abs(n)・・・nの絶対値を取得します。
この絶対値にする方法でできないか考えてみましょう。差分nが負数・正数に関わらずMath.abs(n)は正数になります。ということは絶対値であるMath.abs(n)を切り上げすると見えてくるものがあります。そうです、Math.abs(-2.5)は2.5なので、Math.ceil(Math.abs(-2.5))は3になります。元々の差分は負数なので、Math.ceil(Math.abs(-2.5))に-1を乗算すると-3になります。このことは
nが負数の時はMath.ceil(math.abs(n))*(-1)を求め、
nが正数の時はMath.ceil(math.abs(n))*(+1)を求める
ことから加算する値を求めてやればいいことに気がつきます。
では負数の時は-1を乗算、正数の時は+1を乗算とはどうすればいいか?またif〜文で場合分けしますか?それでしたら先ほどの方法のほうが手っ取り早く思います。
{ヒント}差分が負数の時=>-1に、差分が正数の時に=>1に
なるような計算はないかな?
【答え】
例えば-8.4から-1を得るには-8.4/8.4でできます。8.4から1を得るのは8.4/8.4でできます。n/Math.abs(n)はnが負数の時は-1に、nが正数の時は1になります。
ということでまとめます。
lx=Math.ceil(Math.abs((zx-lx)/2))*(zx-lx)/Math.abs(zx-lx)+lx
まだ未確定ながら関数を書いてみましょう。
function approchX(){
lx=Math.ceil(Math.abs((zx-lx)/2))*(zx-lx)/Math.abs(zx-lx)+lx
mcbox.style.pixelLeft=lx
}
赤い部分ですが、zxがlxより大きい時はzx-lxは正数になります。この差分の半分だけを現在のlxに加算してlxに代入すると移動後の位置が出ますね。
zxがlxより小さい時はどうでしょう?zx-lxは負数になり、この差分の半分も負数ですね。lxの方がzxより大きいわけですから、lxに負数である(zx-lx)/2を加算するとやはりlxはzxに近づきます。
approchYについても考え方は同様なので次のようになります。
function approchX(){
lx=Math.ceil(Math.abs((zx-lx)/2))*(zx-lx)/Math.abs(zx-lx)+lx
mcbox.style.pixelLeft=lx
}
function
approchY(){
ty=Math.ceil(Math.abs((zy-ty)/2))*(zy-ty)/Math.abs(zy-ty)+ty
mcbox.style.pixelTop=ty
}
a=a+bはa+=bと書けるのでlx+=Math.ceil(Math.abs((zx-lx)/2))*(zx-lx)/Math.abs(zx-lx)でもOKです。
Cもし差分の半分が1ピクセルに満たない場合はlxはzxにする、tyはzyにする。これについては既に解決しているようです。Bで差分の半分が少数である場合は切り上げるという説明により差分の半分が1ピクセルに満たない場合は近づける距離は1ピクセルなのですから。差分の半分が1ピクセル未満になるのは差分が1ピクセルの時です。
Dlxとzxが同じである時は実行しない。またtyとzyが同じ場合も実行しない。これは「lxがzxと等しくない場合はapprochX()関数を実行。tyがzyと等しくない場合はapprochY()を実行。」とも同じです。
if(lx!=zx)approchX()
if(ty!=zy)approchY()
E近づけるために行う処理はsetTimeout()で繰り返させる。繰り返し実行する関数を用意してsetTimeout()で繰り返しましょう。
function repeatF(){
if(lx!=zx)approchX()
if(ty!=zy)approchY()
setTimeout("repeatF()",300)
}
さあまとめてテストです。
[ソース]
<HTML xmlns:t="urn:schemas-microsoft-com:time">
<head>
<meta
http-equiv="content-type" content="text/html; charset=shift_jis">
<META
http-equiv="Content-Style-Type" content="text/css">
<title></title>
<STYLE
type="text/css">
<!--
time{behavior:url(#default#time2)}
t\:*
{behavior:url(#default#time2)}
-->
</STYLE>
<script>
var
zx=0
var zy=0
function zahyo(){
zx=event.x
zy=event.y
}
</script>
</head>
<body
onmousemove="zahyo()" style="overflow:auto;
margin:0px; padding:0px">
<div id="mcbox" style="width:100px;
height:100px; position:absolute; top:0px; left:0px; overflow:visible; border:red
1px solid">
<img id="img1"
src="octopus.gif" style="position:absolute">
</div>
<script>
var
lx=mcbox.style.pixelLeft
var ty=mcbox.style.pixelTop
function
approchX(){
lx=Math.ceil(Math.abs((zx-lx)/2))*(zx-lx)/Math.abs(zx-lx)+lx
mcbox.style.pixelLeft=lx
}
function
approchY(){
ty=Math.ceil(Math.abs((zy-ty)/2))*(zy-ty)/Math.abs(zy-ty)+ty
mcbox.style.pixelTop=ty
}
function
repeatF(){
if(lx!=zx)approchX()
if(ty!=zy)approchY()
setTimeout("repeatF()",300)
}
repeatF()
</script>
<t:seq
id="seq0" repeatcount="indefinite">
<t:animatemotion
id="idou1" targetelement="img1" begin="0" dur="1"
path="m 0 0 c 50 0 100 0 150 0" fill="hold" />
<t:animatemotion
id="idou2" targetelement="img1" begin="0" dur="1"
path="m 0 0 c -50 0 -100 0 -150 0" fill="hold" />
</t:seq>
</body>
</html>
次回から円運動、三角関数を扱っていきましょう。