この項では、サンプルサイトの下半分――サムネイルをクリックすると大きい画像がモーダルウインドウで表示される形式のギャラリーを作っていきます。
最終的には表示させたいデータ(使用するサムネイル・原寸画像のパスやキャプションの文章など)を管理用の一つのファイルにまとめて記述しておいて、そこから必要なhtmlを生成する仕組みにしていきたいと思います。
モーダルウインドウを表示させるスクリプトには、doさんで公開されているfuwaimgを使用することにします(配布サイトが日本語で使い方が分かりやすいため)。他のモーダルウィンドウ表示スクリプトでも基本の考え方は共通だと思うので、お好きなものに応用してみてください。
いきなり11tyの機能を組み込んで作っていくのは大変なので、実践編その③ 展示ページの目次を作ろうの最後に述べたように、まずは完全手打ちで作るときと同様にギャラリー部分を作成し、後から自動でhtmlを生成するようなコードに書き換えていくという手順で進めていきます。
最初にfuwaimgの必要なスクリプトをサイト用フォルダに配置します。画像やcssと同様にそのまま_site
フォルダ内にコピーして欲しいので、src
フォルダ内にDL・解凍したfuwaimg
フォルダを丸ごと入れておきましょう。
これで必要なスクリプトの準備はできました。ここから、index.html
にギャラリー部分を作っていきます。
まずは、index.html
内で上記スクリプトの必要なファイルを読み込みます。詳しい説明は配布ページで書かれているのでそちらを参照していただくとして、ここでは実際のコードを示していきます。
まずはfuwaimg.css
を<head>~</head>
内で読み込むために、index.html
で使用しているレイアウトlayout.html
に下記の記述を追加します。
<!-- 前略 -->
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/src/style.css">
+ <link rel="stylesheet" href="/src/fuwaimg/css/fuwaimg.css"> <!-- この行を追加 -->
<title>{{ title }} - Sample Site</title>
</head>
<!-- 後略 -->
次は必要なjavascriptファイル2つをindex.html
の最下部で読み込みます。(layout.html
に書いてもいいのですが、他のページでもlayout.html
を使っていた場合、必要ないのにスクリプトを読み込むことになって無駄な感じがするので、使用するテンプレートファイルの方に記述しています この辺りはお好みで)
<!-- 前略 -->
<section>
<h2>Gallery 2/モーダルを使用した例</h2>
<p>モーダルウィンドウを使用した画像ギャラリーをここに作る予定</p>
</section>
<!-- 以下の2行を追加 -->
+<script src="/src/fuwaimg/js/scrollbooster.min.js"></script>
+<script src="/src/fuwaimg/js/fuwaimg.js"></script>
これで必要なファイルは読み込めたので、ギャラリーを表示するためのコードを書いていきましょう。とりあえず動作確認も兼ねて3~4つ程度画像を置いてみます。こちらも詳細な説明はdoさんのfuwaimg配布ページにお任せするとして、実際のコードを以下に示します。(画像は各自お好きなものを用意してください。以下のコードでは、サムネイルに使用する画像は正方形にトリミングしてsrc/square
フォルダ内に格納してある前提で書いています。)
<!-- 前略 -->
<section>
<h2>Gallery 2/モーダルを使用した例</h2>
<ul class="gallery_2">
<li>
<a href="/src/001.jpg" data-fimg="gallery2" data-fcaption="ここにキャプションを入れることができます" class="fuwaimg">
<img src="/src/square/001_sq.jpg" alt="">
</a>
</li>
<li>
<a href="/src/002.jpg" data-fimg="gallery2" data-fcaption="2枚目" class="fuwaimg">
<img src="/src/square/002_sq.jpg" alt="">
</a>
</li>
<li>
<a href="/src/003.jpg" data-fimg="gallery2" data-fcaption="" class="fuwaimg">
<img src="/src/square/003_sq.jpg" alt="">
</a>
</li>
<li>
<a href="/src/004.jpg" data-fimg="gallery2" data-fcaption="キャプションはなくても可" class="fuwaimg">
<img src="/src/square/004_sq.jpg" alt="">
</a>
</li>
</ul>
</section>
<!-- 後略 -->
基本は以下のようなコードを表示させたい画像の分だけ<ul></ul>
内で繰り返しているだけです。
<li>
<a href="表示したい画像のパス" data-fimg="グループ名(半角英数字)" data-fcaption="ここにキャプション" class="fuwaimg">
<img src="サムネイル画像のパス">
</a>
</li>
ではここで、一度11tyのローカルサーバーを起動して、ちゃんとギャラリーが表示できるか確認しておきましょう。
サムネイルが並んでいるのでクリックして……
このように表示されればOKです。それでは、自動でhtmlを生成する機能を組み込んでいきましょう。
今回は、データを管理するためのファイルとしてGlobal Data Filesという仕組みを利用します。
Global Data Filesとは、ある特定のフォルダ内(デフォルトでは_data
という名前のフォルダ)にJSONファイルを配置しておくことで、そのJSONファイルのデータをサイト内のどこからでも参照できるようになる仕組みです。
例えば、下記のように_data
フォルダの中にlist.json
というファイルを作成し、
このファイルの中身にJSON形式でデータを記述しておくと、サイトのどのテンプレートからでも
list
というキーワードでその中身のデータを参照できるようになります。
この仕組みを利用して、ギャラリーに表示させるデータを1つのファイルで管理できるようにしてみます。
ここで、ギャラリーを構成するためにどのようなデータを持っておけばいいのか、整理してみましょう。 まずは普通に書いてみるの項目でも述べたように、ギャラリーは以下のようなコードを表示させたいコンテンツの分だけ繰り返すことで構成されていました。
<li>
<a href="表示したい画像のパス" data-fimg="グループ名(半角英数字)" data-fcaption="ここにキャプション" class="fuwaimg">
<img src="サムネイル画像のパス">
</a>
</li>
このコードを見てみると、1つのコンテンツにつき必要な情報は以下の3つであることが分かります。
つまり、1つのコンテンツにつき上記3つの情報を1つのまとまりとして、それをコンテンツの分だけ並べておけばよさそうです。以下の図のようなイメージです。
そして、上記のイメージをJSON形式に置き換えるとこのような形になります。
[
{
"imagePath": "表示したい画像①のパス",
"caption": "①のキャプションの文章",
"thumbnailPath": "サムネイル画像①のパス"
},
{
"imagePath": "表示したい画像②のパス",
"caption": "②のキャプションの文章",
"thumbnailPath": "サムネイル画像②のパス"
},
//(...中略...)
{
"imagePath": "表示したい画像のパス",
"caption": "キャプションの文章",
"thumbnailPath": "サムネイル画像のパス"
}
]
{}
で囲まれた一塊が一つのコンテンツに対応しており、それをコンテンツの数だけ,
(コンマ)で区切って一番外側の[]
の中に並べていく感じです。
それでは、次の項でギャラリーを生成するための具体的なコードを記述していきましょう。
まずは表示させたいコンテンツのデータをまとめるためのGlobal Data Fileを作成します。サイト制作用フォルダの直下に_data
という名前のフォルダを作り、その中にcontents.json
というファイルを作成します。
このcontents.jsonの中に表示させたいコンテンツのデータを記述していきます。画像のパスやキャプションの中身などは任意で設定してください。また、キャプションを書きたくないコンテンツがあるときは、
"caption"
の行を消すのではなく、対応する値を空の文字列""
にしておきます。
[
{
"imagePath": "/src/001.jpg",
"caption": "001.jpgのキャプション",
"thumbnailPath": "/src/square/001_sq.jpg"
},
{
"imagePath": "/src/002.jpg",
"caption": "002.jpgのキャプション",
"thumbnailPath": "/src/square/002_sq.jpg"
},
{
"imagePath": "/src/003.jpg",
// ↓キャプションが不要なときの例
"caption": "",
"thumbnailPath": "/src/square/003_sq.jpg"
},
//(...中略...)
{
"imagePath": "/src/XXX.jpg",
"caption": "XXX.jpgのキャプション",
"thumbnailPath": "/src/square/XXX_sq.jpg"
}
]
次に、このcontents.json
のデータをindex.html
内に読み込んでいきます。いきなりギャラリー表示用のコードに組み込んでいくのは少し骨が折れるので、とりあえずデータを読み込んで文字列として表示させてみましょう。
Global Data Fileはファイル名をキーとしてデータを参照することができるのでした。ですので、今回は{{contents}}
と書くことで上記のデータを参照することができるようになります。
さらに、今回の記述方法だとcontents
の中身は配列となっており、{{contents[順番を表す数値]}}
とするとその位置に当たる要素(要素…ここでは{}で囲まれた1つの塊のこと)を参照することができます。この順番は0から始まって1,2,……とカウントするため、contents[0]
とすると一番最初の要素が取り出せます。
そしてcontents[0]
の後に.属性の名前
と続けると、要素内のその属性に対応した値を取り出すことができます。今回の例では、imagePath
, caption
, thumbnailPath
が属性に当たります。これを踏まえてcontents.json
内で1番上に記述したコンテンツに対する画像のパス、キャプション、サムネイルのパスをindex.html
で表示させてみます。
<!-- 前略-->
<section>
<h2>Gallery 2/モーダルを使用した例</h2>
<p>
画像のパス:{{contents[0].imagePath}}<br>
キャプション:{{contents[0].caption}}<br>
サムネイルのパス:{{contents[0].thumbnailPath}}
</p>
<ul class="gallery_2">
<!-- 後略 -->
結果
無事に
contents.json
内に記述したデータの、1番最初の要素を取り出すことが出来ました。また、上記のコードの[]
内の数字を1,2,3,...と変えていくとcontents.json
内の2番目の要素、3番目の要素、4番目の要素、……と取り出す要素を変えることができます。ローカルサーバーを起動した状態で数字を書き換え→上書き保存して、挙動を確認してみましょう。
これでデータの取り出し方は分かったので、いよいよこのデータを最初に記述したギャラリーのコードに組み込んでいきます。 前述の通り、ギャラリーは以下のようなコードを表示させたいコンテンツの分だけ繰り返すことで構成されています。
<li>
<a href="表示したい画像のパス" data-fimg="グループ名(半角英数字)" data-fcaption="ここにキャプション" class="fuwaimg">
<img src="サムネイル画像のパス">
</a>
</li>
繰り返しということは、実践編その③ 展示ページの目次を作ろうでも使用したfor文が使えそうです。
{% for xxx in yyy %}
この間に繰り返したいコードを書く
{% endfor %}
ここで、繰り返しの対象となる配列はcontents.json
から取り出したcontents
、「繰り返したいコード」は上で示した<li>~</li>
のコードですのでそのままfor文に当てはめると……
{% for item in contents %}
<li>
<a href="表示したい画像のパス" data-fimg="グループ名(半角英数字)" data-fcaption="ここにキャプション" class="fuwaimg">
<img src="サムネイル画像のパス">
</a>
</li>
{% endfor %}
そして、{% for item in contents %}
~{% endfor %}
の中では、配列contents
内の1つ1つの要素(上でcontents[0]
やcontents[1]
などとして取り出していたデータ)はitem
という名前で扱うことができます。さらにこのデータ内に含まれる画像のパスやキャプションなどは.属性の名前
と付けることで取り出すことが出来ました。
これによって上記のコード内の"表示したい画像のパス"
"ここにキャプション"
などの部分を置き換えると……
{% for item in contents %}
<li>
<a href="{{item.imagePath}}" data-fimg="グループ名(半角英数字)" data-fcaption="{{item.caption}}" class="fuwaimg">
<img src="{{item.thumbnailPath}}">
</a>
</li>
{% endfor %}
このように書けばいいことが分かります。あとは「グループ名」の部分を任意の文字列にしてやればOKです。
それでは実際にindex.html
の該当部分を上記のコードに置き換えてみましょう。
<!-- 前略 -->
<section>
<h2>Gallery 2/モーダルを使用した例</h2>
<ul class="gallery_2">
{% for item in contents %}
<li>
<a href="{{item.imagePath}}" data-fimg="gallery2" data-fcaption="{{item.caption}}" class="fuwaimg">
<img src="{{item.thumbnailPath}}">
</a>
</li>
{% endfor %}
</ul>
</section>
<!-- 後略 -->
この状態で生成されたページをブラウザで確認してみると……
contents.json
に記述した分だけ、ちゃんとサムネイルが表示されました。さらにそれぞれクリックするとちゃんとモーダルウィンドウで原寸画像の表示・前後への移動もできるようになっているはずです。また、contents.json
内に
{
"imagePath": "/src/XXX.jpg",
"caption": "XXX.jpgのキャプション",
"thumbnailPath": "/src/square/XXX_sq.jpg"
}
この形式をひとまとまりとしてデータを追加したり削除したりすれば、それに伴ってギャラリーに表示されるコンテンツも増えたり減ったりします。ぜひ試してみましょう。
ここで、サムネイルの表示順を確認してみると、contents.json
内で上に記述したものが先、下に記述したものが後になっています。データ内を上から下へ順番に処理しているということですね。もしもこれを逆にしたければ、日付データの扱い方にも使用した{% for ... reversed %}
の書き方が使えます。もちろん、最初からcontents.json
内の並びを希望の順番にしておくのもよいでしょう。この辺りは、更新の際にデータを上に追記していくのか下に追記していくのかも併せて、ご自身のやりやすい・分かりやすい方法を採用すればよいと思います。
これで、最初に示したサンプルサイトは完成です🎉🎉🎉お疲れさまでした!
サンプルサイト(完成品)のテンプレートファイル及び設定ファイルのソースコードを置いておきます
サイト制作用フォルダ直下の構成はこんな感じです
module.exports = function (eleventyConfig) {
eleventyConfig.addGlobalData("permalink", () => {
return (data) => `${data.page.filePathStem}.${data.page.outputFileExtension}`;
});
eleventyConfig.addPassthroughCopy("src");
};
---
title: Index
layout: layout.html
---
<img src="src/header.jpg">
<section>
<h2>About</h2>
<p>ここにサイトの説明とか注意書きとか書いておくといいですね</p>
</section>
<section>
<h2>Gallery 1/個別ページの例</h2>
<p>展示ページの目次がここに来る予定</p>
<ul class="gallery_1">
{% for item in collections.gallery reversed %}
<li>
<a href="{{ item.page.url }}">
<img src="{{item.data.thumbnail}}" alt="">
<span class="thumbnail_caption">
{{ item.data.title }}<br>
<small>{{ item.page.date | date: "%Y/%m/%d" }}</small>
</span>
</a>
</li>
{% endfor %}
</ul>
</section>
<section>
<h2>Gallery 2/モーダルを使用した例</h2>
<ul class="gallery_2">
{% for item in contents %}
<li>
<a href="{{item.imagePath}}" data-fimg="gallery2" data-fcaption="{{item.caption}}" class="fuwaimg">
<img src="{{item.thumbnailPath}}">
</a>
</li>
{% endfor %}
</ul>
</section>
<script src="/src/fuwaimg/js/scrollbooster.min.js"></script>
<script src="/src/fuwaimg/js/fuwaimg.js"></script>
[
{
"imagePath": "/src/001.jpg",
"caption": "001.jpgのキャプション",
"thumbnailPath": "/src/square/001_sq.jpg"
},
{
"imagePath": "/src/002.jpg",
"caption": "002.jpgのキャプション",
"thumbnailPath": "/src/square/002_sq.jpg"
},
{
"imagePath": "/src/003.jpg",
"caption": "003.jpgのキャプション",
"thumbnailPath": "/src/square/003_sq.jpg"
},
// ...中略...
// 実際のソースコードではコメント行は削除してください
{
"imagePath": "/src/011.jpg",
"caption": "011.jpgのキャプション",
"thumbnailPath": "/src/square/011_sq.jpg"
}
]
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/src/style.css">
<link rel="stylesheet" href="/src/fuwaimg/css/fuwaimg.css">
<title>{{ title }} - Sample Site</title>
</head>
<body>
{% include "partials/header.html" %}
{{ content }}
{% include "partials/footer.html" %}
</body>
</html>
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="/src/style.css">
<title>{{ title }} - Sample Site</title>
</head>
<body>
{% include "partials/header.html" %}
{{ content }}
{% assign previousPost = collections.gallery | getPreviousCollectionItem: page %}
{% assign nextPost = collections.gallery | getNextCollectionItem: page %}
<section>
<nav class="navInCollection">
<ul>
{% if nextPost %}
<li class="navPrev">
<a href="{{ nextPost.page.url }}">{{ nextPost.data.title }}</a>
</li>
{% endif %}
{% if previousPost %}
<li class="navNext">
<a href="{{ previousPost.page.url }}">{{ previousPost.data.title }}</a>
</li>
{% endif %}
</ul>
</nav>
</section>
{% include "partials/footer.html" %}
</body>
</html>
<header>
<h1><a href="/">Sample Site</a></h1>
</header>
<footer>
<h4>Sample Site</h4>
<p>
<small>© 2024 yoshimura</small>
</p>
<ul class="link_footer">
<li>
<a>
<span class="material-symbols-outlined">home</span>
</a>
</li>
<li>
<a>
<span class="material-symbols-outlined">mail</span>
</a>
</li>
<li>
<a>
<span class="material-symbols-outlined">favorite</span>
</a>
</li>
<li>
<a>
<span class="material-symbols-outlined">onsen</span>
</a>
</li>
</ul>
</footer>
---
title: 展示ページ①
thumbnail: /src/thumbnails/001.jpg
date: 2024-01-23
---
<section>
<img src="/src/001.jpg" alt="">
<h2>{{ title }}</h2>
<p>けれどもぼくは、そんなことを習ったろうと思いながら答えました。僕いま苹果のことを考えているんですそうですか。けれども、誰だって、ただそう感じているのでした。つりがねそうか野ぎくかの花があちこち咲いていました。時計屋の店には明るくネオン燈がついて、たくさんのきいろな底をもった人に出しました。</p>
</section>
<!-- ファイルの数や内容は任意 -->
{
"layout": "gallery.html",
"tags": "gallery"
}
ここまで目を通していただきありがとうございました。
当サイトの制作に使用しているSSGの11tyについて、必要なツールのDLからちょっとしたサイトを構築するまでの流れを一通り解説してみました。基本的なことや創作系の個人サイトで使いそうな機能にしぼって説明したため、ここで紹介できていない機能や説明が十分でない概念なども色々あります。気になる部分や深く知りたい部分などあれば、ぜひググったり、文中で紹介している公式ドキュメントを参照したりしてみてください。(公式ドキュメントは英語ですが、今どきは優れた翻訳ツールもあるので恐れることはないぞい)
この解説が、サイト制作・改装に関する何らかのヒントや足掛かりになれば幸いです。