2011 11 03

スパイダーを作るの3

昨日の続き。 カードの移動を考える。

本家では、列から列へのカードの移動は、ドラッグ/ドロップになっている。 移動するときの手順を分解すると、大雑把に次の3操作。

  1. 移動元の列の移動する末尾のカードの上で、マウスボタンを押す。
  2. マウスボタンを押したままカーソルを移動する。
  3. 異動先の列のカードの上で、マウスボタンを離す。

このそれぞれでやる処理の詳細は、準備も含めて大体次の通りになるだろう。

移動可否判定の準備

マウスボタンが押されたときに処理が実行できるように、イベント処理を設定しておく。 ただし、イベント処理をどう設定するかは考えるところ。 例えば、

全てのカードに共通して設定する。
この場合、クリックされたときに、そのカードがどう振る舞うかの判定をすることになる。 ここで言う振る舞いとは、列間の移動だけでなく、未配カード群からのカード配布も含む。
列に出ている全てのカードに設定する。
クリックされたときに、そのカードがどう振る舞うかを判定するのは同じだが、判定内容は列の移動可否のみ。 列にカードを配る時にイベント処理を設定し、13枚揃って完了カード群に移動するときに解除することになるだろう。
表向きのカードのみ設定する。
そもそも移動できるのは表向きのカードだけなので、裏向きのカードのイベント処理は止めてしまう。 この場合、列に表向きのカードを配るときと、移動後の列の先頭でカードを表向きにするときに、イベント処理を設定することになる。
移動可能なカードのみ設定する。
そもそも移動できるのは移動可能なカードだけなので、移動できないカードのイベント処理は止めてしまう。 カードを配った時、各列で移動可能かどうかを併せて判定しておき、ここで移動可能となったものにイベント処理を設定しておく。 移動不可となったカードにイベント処理が設定されていたら、解除しておく。 いや、移動可否判定の前に、その列に設定済みのイベント処理を全て解除しておくのが楽かな。
カードの移動可否判定(移動元)

移動元でマウスボタンが押されたとき、列の先頭からカーソル位置のカードまでが、同じマークの連番昇順であるかどうかを判定する。 この条件を満たしていれば、移動可能として次の処理を行う。

条件を満たさなかった場合にどうするかは、上記の準備をどのパターンで実装するかによる。 クリックイベントの中で判定する場合は、 「条件を満たさなければ何もしない」 という処理をすることになる。 移動可能なカードのみイベント処理を設定する場合は、条件を満たさない場合は本当に何もしなくていい。

カードの移動準備

マウスカーソルの移動に対してドラッグのためのイベント処理を設定する。 ここで、ドラッグを 「移動対象のカードとマウスカーソルの相対位置を保つこと」 と考えて、実際の処理をもう少し細かくすると、

  1. マウスカーソルがクリックされた位置と、移動対象カードとの座標差を取得する。
  2. この座標差を保ってマウスカーソルに追随するように、移動対象カードに位置を設定するイベント処理を設定する。

となるだろう。

カードの移動

上記イベント処理を行う。

カードの移動可否判定(移動先)

マウスボタンを離したとき、そこにカードを移動できるかどうかは、次の条件を満たすかどうか。

  • 移動元とは違う列のカードの上であること。
  • その列の先頭のカードの数字が、移動してきた末尾のカードの数字より1大きいこと。 或いは、移動先の列が空であること。
カードの列への移動

上記判定の結果で処理が分かれる。

  • 移動可能な場合、移動してきたカードを列の先頭に追加する。
  • 移動不可の場合、元の列の元の位置にカードを戻す。

と、ここまで考えたところで方針転換。 俺はこのドラッグ/ドロップ方式がどうも好きじゃないのだ。 マウスを使うときはこれでもいいのだが、トラックパッドだと幅が足りなくてやり直しになることもしばしば。 ということで、移動元と移動先をクリックで決めることにした。 具体的にはこんな感じ。

移動可否判定の準備

表向きのカードにイベント処理を設定する。 イベント処理のタイミングは上に同じ。

カード移動可否判定(移動元)

移動元を選択中かどうかで処理を分ける。

移動元が選択されていない場合
  • 移動元の列とする。
  • 列の先頭からクリックされたカードまでのうちで移動可能なカード全てを仮の移動対象とする。
  • 列の先頭のカードを選択中表示とする。
移動元が選択されている場合

今回選択した列が移動元と同じかどうかで処理を分ける。

移動元と同じ列の場合
  • 移動元の選択を解除する。
  • 仮の移動対象を解除する。
  • 列の先頭の選択中表示を解除する。
移動元と異なる列の場合

カード移動可否判定(移動先)を行う。

カード移動可否判定(移動先)

移動先として選択した列にカードがあるかどうかで処理を分ける。

カードがあるとき

仮の移動対象に、列の先頭のカードより1少ないカードが有ること。 有れば、仮の移動対象の先頭からそのカードまでを、実際の移動対象とする。

空のとき

仮の移動対象の全てを、実際の移動対象とする。

カードの移動
移動元でやること
  • 実際の移動対象を取り去る。
  • 仮の移動対象を解除する。
  • 移動元の列を解除する。
  • 列の先頭が裏向きの場合は表向きにする。 このとき、イベント処理も設定する。
移動先でやること
  • 実際の移動対象を、順番を保ったままで、列の先頭につなげる。
  • 13枚揃っていれば、完了カード群への移動を行う。

選択中のうちで移動できるものを全て移動するのは、その方が操作性がいいような気がしたから。 移動元と先の列の選択で、移動できるものを全て移動することも考えたが、これだと分割して移動することが出来ない。 かと言って、いつもいつも移動できるものをきっちり選択するのも、iPhone なんかの小さい画面だとかなり面倒。 で、間を取ってこの仕様。

おっと、カードにイベント処理を設定していると、カードがなくなって列が空になった時に、その列を移動先に指定できなくなるじゃないか。 うーん…

列のカードだけでなく、列そのものにイベント処理を設定するか。 いや、いっそイベント処理はカードではなく列に持たせるか。 カードを配置するエリアを包含するようにdivを設定して、伝播してきたイベントを処理するとか。 あ、でも、これだとカードの移動がDOMの要素ツリーの組み替えになって面倒そうだな。 うーん…

列を本当に空にするのではなく、一番下に移動できないダミーのカードを置いて、これが先頭のときを空と判断するか。 これならカードだけで統一的に扱えるし。 でも、ダミーのカードってのが、なんだかなぁ。 あ、でも本家のは、列が空になったら枠が見えるようになっているから、ダミーのカードをこの枠に見立てればいいのか。 ダミーのカードを、数字もマークも無い枠だけの白いカードとしておけば、きっと枠に見えるだろう。 いや、見えるか見えないかじゃない。 見るのだ。

と、決意を新たにして、今日はここまで。