検索
連載

Pinterest風グリッドレイアウトを作ってみようjQuery×HTML5×CSS3を真面目に勉強(3)(1/4 ページ)

jQueryを使って、女性に人気の写真共有SNS、Pinterestのグリッドレイアウトを再現してみよう。

PC用表示 関連情報
Share
Tweet
LINE
Hatena

はじめに

 一口にSNSというと、読者の皆さんはまず、TwitterやFacebook、LINEなどのサービスを思い浮かべることでしょう。しかし、こういったデファクトスタンダードなサービスだけでなく、世の中にはさまざまなジャンルに特化したSNSがいくつも登場してきています。

 特に写真共有に特化したSNSのUIというのは、その特性のためか画像を画面いっぱいに敷き詰めたものがよく見受けられます。基本的な操作性はもちろんのこと、いかに美しく、ユニークに画像を並べるかによって他サービスとの差別化を図るかが、デザイナーならびにフロントエンドエンジニアの腕の見せ所といっても過言ではありません。

 Pinterestは、そうした写真共有SNSの1つです。高さが均一のグリッドが整然と並べられている従来型のグリッド式レイアウトとは違い、異なる高さのグリッドが画面いっぱいに敷き詰められているレイアウトが特徴的でお洒落な印象です。

Pinterestとは?

 Pinterestとはピンボード風の写真共有SNSです。特に女性に人気があるといわれています。Webサイトとアプリでは、テーマに基づいた写真のコレクションを作ることができます。同サービスのミッションステートメントは「面白いと感じるものを通じて世界全員をつなぐ」。米国パロアルトに拠点を置くCold Brew Labsによって運営されています(参考:http://ja.wikipedia.org/wiki/Pinterest)。

 そんな女子力の高いSNSであるPinterest。今回は、このグリッドレイアウトを再現するための手順をご紹介したいと思います。

グリッドレイアウトの要件を定義しよう

 いきなりコードを書き始める前に、まずはPinterest風グリッドレイアウトの要件を大まかに書き出してみます。

  1. 高さの異なる各グリッドをタテ・ヨコに等間隔で配置したい
  2. 配置されるグリッドは親要素であるコンテナの幅いっぱいに敷き詰められ、リキッドレイアウトに対応する
  3. グリッドは左から右に向かって配置され、1行に収まり切らない場合は折り返して次の行に配置される
  4. 2行以降の配置は左端から順番に配置するとは限らず、Topからの距離が一番短い位置に配置される
  5. 以降のグリッドもTopからの距離が一番短い位置から順に配置される(※必ずしも2行目が全て埋まってから3行目に配置されるとは限らない)。
  6. ウィンドウサイズを変更することによって、列数とグリッドの配置をアニメーションで動的に変更する

 いくつか補足説明をします。

 従来のグリッドレイアウトのルールに習うと各グリッドは以下の図1のように1 → 2 → 3 → 4 (改行) 5 → 6 → 7 → 8 etc...といったように左から右に向かって順に配置されていきます。通常ならこれで何の問題もありませんが、今回のPinterest風グリッドでは大きな問題があります。


図1

 図1ではグリッド3が他のグリッドと比較して縦に長くなっています。この程度なら大して問題にはなりませんが、仮にブラウザをスクロールしなくてはならないほど縦に長かったらどうでしょうか? 5 → 6と来て次のグリッド7はスクロールしないと見ることができないという状態になってしまいます。そこでPinterestでは以下の図のような順序で各グリッドを配置しています。


図2

 お分かりいただけたでしょうか?

 グリッド6までは図1と同じ並びで配置されていますが、グリッド7とグリッド8の左右の順序が逆になっています。これは2行目以降はTopからの距離が一番短い位置(※一番高位置に配置できる所)から優先してグリッドが配置されていくためです。Pinterestでは、ピンボード風の写真共有SNSというコンセプトが優先されているためか、ユーザーに対して順序を明確に認識させることを重要視せず、このような配置を採用しているのではないかと推測されます。

 では、これより実装していきます。

プログラムを書く準備

 以下の環境を用意しましょう。

  • UTF-8が扱えるテキストエディタ
  • Webブラウザ(※この連載ではChromeを標準として進めていきます

サンプルファイルのダウンロード

 今回のサンプルファイルは「wakamsha/learning-pinterestgrid - GitHub」からダウンロードできます。必要に応じてご参照ください。

jQueryの読み込みについて

 HTMLファイルにてjQueryファイルを読み込む際は、以下のコードのようにオフィシャルサイトのCDNを利用します。

<script src="http://code.jquery.com/jquery-2.0.0.min.js"></script>
HTML

 ファイルそのものを直接ローカル環境にダウンロードしても問題ありませんが、管理するファイルが増えるのも手間なので、ネットワーク上から直接読み込む方法を推奨します。

グリッドレイアウトを実装

HTMLをマークアップ

 まずはHTMLから書いていきます。

<div id="container">
    <div class="grid">
        <div class="imgholder">
            <img src="http://media-cache-ak1.pinimg.com/192x/af/96/f6/af96f6f48b4b5a2a57b1a334c16d7b2b.jpg">
        </div>
        <h2>1 | Photo title</h2>
        <p>elisa french lace veil - enchanted atelier fall winter 2013 collection</p>
        <div class="meta">by Naoki Wakamsha Yamada.</div>
    </div>
    <div class="grid">
        <div class="imgholder">
            <img src="http://media-cache-ec4.pinimg.com/192x/ac/bb/24/acbb240674e70d8b837e857b90b7c224.jpg">
        </div>
        <h2>2 | Photo title</h2>
        <p>Bead Board + Letters: What a sweet and personal way to decorate.</p>
        <div class="meta">by Naoki Wakamsha Yamada.</div>
    </div>
    <div class="grid">
        <div class="imgholder">
            <img src="http://media-cache-ec3.pinimg.com/192x/34/c5/e2/34c5e2172503fa9a5aa719ee4e541202.jpg">
        </div>
        <h2>3 | Photo title</h2>
        <p>For a foster child who's been passed around from home to home, a little piece of hope is lost at each transition. This week, your support will give them a duffel ...</p>
        <div class="meta">by Naoki Wakamsha Yamada.</div>
    </div>
    etc ・・・
</div>
HTML

 これといって特別な記述はしていません。各グリッドに対してgridクラスを指定し、これらをcontainer要素で囲っています。gridクラス要素内の構造は後述するスタイルに合わせて適当に書いただけで、このとおりにする必要は特にありません。

 ちなみにimgタグに指定している画像URLは本家サイトの画像パスを適当に拝借しています。

グリッドをCSSでデザイン

 各グリッドのデザインとそのコンテナ要素のスタイルを書いていきます。

#container {
  margin: 0 auto 25px;
  position: relative;
  padding-bottom: 10px;
}
 
.grid {
  background: #fff;
  box-shadow: 0 1px 3px rgba(34, 25, 25, 0.4);
  float: left;
  font-size: 12px;
  margin: 8px;
  min-height: 100px;
  padding: 15px;
  width: 188px;
  transition: top 1s ease, left 1s ease;
}
.grid h2 {
  border-bottom: 1px solid #ccc;
  color: #fa3599;
  display: block;
  font-family: 'Donegal One', serif;
  font-size: 17px;
  margin: 10px 0;
  padding: 0 0 5px;
}
.grid .meta {
  color: #777;
  font-style: italic;
  text-align: right;
}
.grid .imgholder img {
  background: #ccc;
  display: block;
  max-width: 100%;
}

 2013年6月の本稿執筆時点では、Transitionsプロパティに対してベンダープレフィックスをつけて指定する必要があります。本稿では可読性を考慮して省略しております。

 また、22行目にてフォントファミリーを指定していますが、これはGoogle Web Fontsを使用しています。

 ここまでで基本的なデザインは完成しました。いよいよここから、JavaScriptを駆使してレイアウトの実装を行っていきます。

Copyright © ITmedia, Inc. All Rights Reserved.

       | 次のページへ
PREVIEW
'; }else{ mask.innerHTML = '
画像をご覧いただくには会員登録が必要です
' + btn_txt + '
'; } if((_preview && location.hash.indexOf('maskoff') !== -1) || (typeof itmIdLogin !== 'undefined' && itmIdLogin == 1)){ img.style.visibility = 'visible'; }else{ nxt.parentNode.insertBefore(mask,nxt); } } } }; (function(d){ var _preview = d.domain.match(/(preview|broom|localhost)/); window.addEventListener('load',function(){ // islLogin 呼出済|preview if(d.getElementById('isLogin') || _preview){ mask_images({ sc:'0c1c43111448b131d65b3b380041de26f2edd6264ee1c371184f54d26ab53365', lc:'7d7179c146d0d6af4ebd304ab799a718fe949a8dcd660cd6d12fb97915f9ab0a', ac:'1a599d548ac1cb9a50f16ce3ba121520c8ab7e05d54e097bfa5b82cb5a328a0f', cr:'2c93f81754142e105c8bca17824745d14c8c4d69e9d7ede513e5530546e97641', bc:1 }); // islLogin なし }else{ var js = mask_images.setISLOGIN('//status.itmedia.co.jp/isLoginAIT.cgi','0c1c43111448b131d65b3b380041de26f2edd6264ee1c371184f54d26ab53365'); js.addEventListener('load',function(){ mask_images({ sc:'0c1c43111448b131d65b3b380041de26f2edd6264ee1c371184f54d26ab53365', lc:'7d7179c146d0d6af4ebd304ab799a718fe949a8dcd660cd6d12fb97915f9ab0a', ac:'1a599d548ac1cb9a50f16ce3ba121520c8ab7e05d54e097bfa5b82cb5a328a0f', cr:'2c93f81754142e105c8bca17824745d14c8c4d69e9d7ede513e5530546e97641', bc:1 }); }); } }); })(document);
LOADING
'; w.removeEventListener('scroll',arguments.callee,false); htmlRequest(_xhrfile,_idname); elem.setAttribute('data-status','true'); console.log('finished : ' + _idname); }else{ // console.log('retry : ' + _idname); } }else{ e_loader.innerHTML = '
LOADING
'; w.removeEventListener('scroll',arguments.callee,false); htmlRequest(_xhrfile,_idname); elem.setAttribute('data-status','true'); console.log('finished : ' + _idname); } } }; w.addEventListener('scroll',scrolling,false); // スクロールイベント scrolling(); // スクロールイベント(閲覧位置が半端な場合のために 1 回実行させる) }; w.addEventListener('load',loading,false); // LOAD 後に実装 };