本文へジャンプ

算数ドリル ... 動的レイアウトを考えよう

Posted by namio.araki

算数ドリル(動的レイアウトを考えよう)

頭の体操

最近ツール紹介の記事が多かったので、少し頭の体操がてらロジックについて話したいと思います。
Pinterestに代表されるような可変レイアウトだったり、数が決められない(CMSからシステムアウトする)メニューやツールチップなど、一見複雑そうなレイアウトになると、

  • つい似たようなライブラリがないか?
  • ひたすらif文やswitch文でハードコーディングするしかない!

などと考えがちですが、実は簡単な算数で計算されていることも多いです。

お題

今回は要素の折り返し最大数は決めたいけど、まとまってレイアウトされたいよね。っていうのを考えたいと思います。何を言っているかわからないでしょうが、w
例えば
「最大4つで折り返す」
というレイアウトを考えたとしても、1行目と2行目がアンバランスなのは格好悪いので

  • 3つのときは、(上3つでなく)上2つ、下1つとレイアウトされてほしい
  • 5つのときは、(上4つ、下1つでなく)上3つ、下2つとレイアウトされてほしい
  • 7つのときは、上4つ、下3つとレイアウトされてほしい

ということですね。

何に使うの?
という声も聞こえてきそうですが、
ボタンクリック後に出てくるツールチップメニューなどボタンを中心にまとまっていてほしいですし、
画面サイズ/カラムによって、レイアウトを組むときにも応用できるでしょうし、
動的に何かを制御するときの考え方としては入門編かなと思います。
何かと苦手な人の多い sin()、cos()、tan() などの三角関数なども出てきませんし、ビット演算なども出てきません。w

解説

まず考えなければいけないのは、全体の数に対して横何行・縦何列の中に収まるかということです。
最大4列までとするなら、
頭の中では、
3だったら2と1に並べて2行2列、
6だったら3と3で2行3列、
9だったら4と4と1で3行4列
とすぐにでてくるのですが、なかなか計算で出そうと思うと悩むところだと思います。
ただこれさえ理解すればあとはいつものサムネイルを配置するときなどと同じ考え方で大丈夫かと思います。

図 - 算数ドリル

  • 要素の数をOBJ_NUM
  • 最大列数をMAX_COLUMN

とすると、列数COLUMN_NUMと、行数LINE_NUMはそれぞれJSで書くと

var COLUMN_NUM = Math.min(Math.ceil(OBJ_NUM * 0.5 ), MAX_COLUMN);
var LINE_NUM = Math.ceil(OBJ_NUM / COLUMN_NUM);

となります。

解説すると、

  • COLUMN_NUMは、要素の数を半分にした数と最大列数の小さい方の数。
     ※要素の数を半分にわると上の行と下の行の数の差が0か1になるため。
  • LINE_NUMは、要素の数を折り返す数で割った数の切り上げ。
     ※2.2行などになっても、3行目に要素は存在するため切り上げる。

となります。

あとは要素を順番に、配置していくだけです。

var i=0;
var posX, posY; //ボタンを中心(0,0)として要素を配置した際の(x, y)=(posX, posY)とする。
for(; i < OBJ_NUM; i++){
    posX = (i % COLUMN_NUM - COLUMN_NUM * 0.5) * OBJ_WIDTH; //OBJ_WIDTHは要素の横幅
    posY = (Math.floor(i / COLUMN_NUM) - LINE_NUM * 0.5) * OBJ_HEIGHT;  //OBJ_HEIGHTは要素の高さ
}

少しだけ噛み砕くと、
X座標は、
全体の数を横に並べる数で割った余り(i % COLUMN_NUM)番目の列の位置から、
全体の半分の長さを引く(COLUMN_NUM * OBJ_WIDTH * 0.5)。

posX = i % COLUMN_NUM * OBJ_WIDTH - COLUMN_NUM * OBJ_WIDTH * 0.5;

となるので、それを整理すると、

posX = (i % COLUMN_NUM - COLUMN_NUM * 0.5) * OBJ_WIDTH;

Y座標は、
全体の数を横に並べる数で割った数(Math.floor(i / maxW))番目の行の位置から、全体の半分の高さ(LINE_NUM * OBJ_HEIGHT * 0.5)を引く

posX = Math.floor(i / COLUMN_NUM) * OBJ_HEIGHT - LINE_NUM * OBJ_HEIGHT * 0.5

となるので、同じく整理すると、

posY = (Math.floor(i / COLUMN_NUM) - LINE_NUM * 0.5) * OBJ_HEIGHT;

※全体の半分の長さ/高さを引くのは、全体を中心揃えにするため。

どうでしょうか。
今回は3行目の端数は無視してますが、同じような考え方で、9の場合は、上4つ中4つ下1つでなく、上も中も下も3つずつというレイアウトも可能になります。
頭の中では簡単にできることでも、それをロジック的に考えると色々と応用できますので、食わず嫌いせずに頭の体操がてら考えてみるのも一興かと思います。

Recent Entries
MD EVENT REPORT
What's Hot?