2017-03-07

tobijibu

CSSでフィボナッチ数列の螺旋を作成する

デザインの説明サイトを見ていると、よく"黄金比"という言葉が出てきます。 黄金比は最も美しいとされる比率とされ、長方形で表すと、縦:横比が1:1.618...になります。ちなみにその長方形は"黄金長方形"と呼ばれるようです。

黄金比を基準にしてデザインに組み込むと美しくなるということで、様々なデザインに利用されているらしいです。真偽は不明ですがtwitterのアイコンは有名です。 黄金比とデザインの関係性については賛否両論あるようですが。

そこで、黄金比の説明で頻繁に出てくる螺旋図をCSSで作成してみようと思います。ルールは、canvasを使わない、JSを使わない、シンプルな構造で作る、です。



今回作成する螺旋図は大きく分けて2つの図形から成ります。黄金長方形と、正方形です。この2つの図形をいかにして作成するか、ということになります。

まず、一番外側の長方形を作成します。図の大きさを可変にしたいので、横幅基準で高さを設定します。

widthを基準に縦を決めるというのは、一見難しそうですが、実はそうでもありません。 ::before::afterの疑似要素とpadding-toppadding-bottomの組み合わせで実現します。

幅、高さを設定する箇所でいくつかcalcで計算しています。図を綺麗に見せるための微調整なので、calcの計算は無くても大丈夫です。

#golden-ratio {
    position: relative;
    width: 100%;
    height: auto;
    display: table;
    border-collapse: collapse;
    border: solid 1px #333;
}
#golden-ratio::after {
    content: "";
    display: block;
    padding-bottom: calc(61.804% + 0.5px);
}

続いて正方形のブロックです。幅には黄金比の61.804%を指定します。高さは100%です。 これで大きな正方形ができました。ついでに「弧」を描くためのborder-radiusを設定してしまいます。

#golden-ratio div:nth-child(odd) {
    position: absolute;
    width: calc(61.804% - 2px);
    height: calc(100% - 2px);
    border: solid 1px #333;
    border-radius: 100% 0 0 0;
    display: table-cell;
}

次は長方形のブロックです。位置が親要素の左上が基準になっているので、左に100%ずらします。 ずらしただけでは「弧」がつながらないので、transform: rotate90度回転させます。 ただ回転させるだけだと、要素の中心を軸にして回転されてしまい、ずれてしまいます。 transform-originを使って、要素の左上を基準に回転させましょう。

#golden-ratio div:nth-child(even) {
    position: absolute;
    left: 100%;
    width: calc(61.804% - 1px);
    height: 61.804%;
    transform-origin: 0 0;
    transform: rotate(90deg);
    display: table-cell;
}

スタイルの設定はできたので、あとはHTMLです。

2つのdivを並べて、後ろのdivに入れ子構造で入れ込んでいきます。10階層の場合は以下のような構造になります。

<div id="golden-ratio2">
    <!-- 1階層 -->
    <div></div>
    <div>
        <!-- 2階層 -->
        <div></div>
        <div>
            <!-- 3階層 -->
            <div></div>
            <div>
                <!-- 4階層 -->
                <div></div>
                <div>
                    <!-- 5階層 -->
                    <div></div>
                    <div>
                        <!-- 6階層 -->
                        <div></div>
                        <div>
                            <!-- 7階層 -->
                            <div></div>
                            <div>
                                <!-- 8階層 -->
                                <div></div>
                                <div>
                                    <!-- 9階層 -->
                                    <div></div>
                                    <div>
                                        <!-- 10階層 -->
                                        <div></div>
                                        <div></div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

ただ、やっぱりdivの入れ子は面倒なので、今回もここの部分はjsで生成してしまいます。

var setDiv = function() {
  let layer = 10,
    gr    = document.querySelector('#golden-ratio'),
    df    = document.createDocumentFragment(),
    div_odd  = document.createElement('div'),
    div_even = document.createElement('div');
  for (var cnt = 0; cnt < layer; cnt++) {
    div_even = setEven(div_even);
  }
  df.appendChild(div_odd);
  df.appendChild(div_even);
  gr.appendChild(df.cloneNode(true));
};
var setEven = function(div_even) {
  let div = document.createElement('div'),
    _div_even = div_even.cloneNode(true);
  div.appendChild(document.createElement('div'));
  div.appendChild(_div_even);
  return div;
};
setDiv();

これでサイズ可変の黄金比螺旋図ができました。

ただ、大きさを変更すると、微妙にボーダーが重なってしまったり、隙間が空いたりしてしまいます。 本当はボーダーもピッタリ合わせて表示したかったのですが、うまくできませんでした。

高さ、幅の計算誤差によるものなのか、CSSの指定に不備があるのか不明です。 display: tableや、calcで幅指定等の見直しが必要かもしれません。

今回説明で利用したソースはこちらにあります。