ライトボックス Luminous Lightbox の使い方
Luminous はとても軽量でシンプルな jQuery に依存しない画像用ライトボックスプラグイン(JavaScript)です。
以下は Luminous Lightbox(v2.3.5/2021-08-03)の基本的な使い方や、ギャラリー表示やキャプションの表示方法、画像枚数と現在の画像位置(インデックス)の表示、webpack でのバンドル、WordPress で表示する方法などの解説(覚書)です。
Luminous Github:https://github.com/imgix/luminous
作成日:2021年9月29日
インストール
Luminous はダウンロードして読み込むか、npm などを使ってインストールすることができます。
ダウンロード
https://github.com/imgix/luminous の右上の「Code」をクリックして「Download ZIP」をクリックするか、本文中(Installation > Manual) の Download リンクをクリックすると luminous-main.zip という ZIP ファイルをダウンロードできるので、保存して解凍します。
解凍したフォルダ(luminous-main)の中の dist 内の以下の JavaScript と CSS ファイルを使います。
- luminous.min.js
- luminous-basic.min.css
CSS(luminous-basic.min.css)は head 内で読み込みます。「path/to/」の部分は適宜変更します。
<head> ・・・ <link rel="stylesheet" href="path/to/luminous-basic.min.css"> ・・・ </head>
JavaScript は body の閉じタグの直前などで読み込みます。
<script src="path/to/luminous.min.js"></script> ・・・ </body>
luminous.min.js にはソースマップの記述(//# sourceMappingURL=luminous.min.js.map)が最後にあるので、オプションで(必要に応じて)ソースマップファイル(luminous.min.js.map)を luminous.min.js と同じ階層に配置します(読み込む必要はありません)。
WordPress での読み込み
以下は WordPress で使う場合の読み込みの例です。「path/to/」の部分は適宜変更します。
10 行目と18行目は更新時にキャッシュをクリアするための記述ですが、この場合は省略しても良いかと思います。11行目はスクリプトを </body> 終了タグの前に配置するために true を指定しています。
function add_my_styles_and_scripts() {
・・・その他の読み込み・・
//JavaScript の読み込み
wp_enqueue_script(
'luminous-js',
get_theme_file_uri( '/path/to/luminous.min.js' ),
array(), //依存するスクリプトはなし
filemtime( get_theme_file_path( '/path/to/luminous.min.js' ) ),
true //</body> 終了タグの前に配置
);
//CSS の読み込み
wp_enqueue_style(
'luminous-style',
get_theme_file_uri( '/path/to/luminous-basic.min.css' ),
array(), //依存するスタイルシートはなし
filemtime( get_theme_file_path( '/path/to/luminous-basic.min.css' ) )
);
}
add_action('wp_enqueue_scripts', 'add_my_styles_and_scripts');
npm
npmコマンドを使ってインストールする場合はプロジェクトのフォルダで以下を実行します。
npm install luminous-lightbox
webpack
以下は webpack を使ってバンドルする場合のエントリポイントでの記述例です。CSS をバンドルする場合は CSS ローダーや style ローダー(必要に応じて MiniCssExtractPlugin)が必要です。CSS をバンドルしない場合は、別途 head 内で読み込みます。
//Luminous と LuminousGallery(ギャラリー表示用)のインポート(ES6)
import { Luminous, LuminousGallery } from 'luminous-lightbox';
/* 以下は require を使って読み込む場合
const Luminous = require('luminous-lightbox').Luminous;
const LuminousGallery = require('luminous-lightbox').LuminousGallery;
*/
//CSS のインポート(CSS ローダーが必要)
import 'luminous-lightbox/dist/luminous-basic.min.css';
関連ページ:webpack の基本的な使い方
Luminous の使い方
基本的な使い方は JavaScript で Luminous を初期化し、HTML でライトボックス表示する画像を任意のクラス名を設定した a 要素で囲み、href 属性に拡大表示(ポップアップ)する画像のパスを指定します。
画像を a 要素で囲むことが多いですが、画像以外の要素を囲んでその要素をクリックして画像を表示することもできます(アイコンをクリックして表示)。
Luminous にはそのページで1つの画像のみに適用する方法(単体表示)と複数の画像に適用する方法(ギャラリー表示)があります。
単体表示
対象の img 要素を任意のクラス名(以下の場合は luminous)を設定した a 要素で囲みます。a 要素の href 属性には拡大表示する画像のパスを指定します。
以下の場合、01-w600.jpg は表示画像(サムネイル)で、01-w1800.jpg は拡大表示する画像です(表示画像と拡大画像に同じ画像を指定することもできます)。パスは適宜環境に合わせて変更します。
<a class="luminous" href="../img/01-w1800.jpg"> <img src="../img/01-w600.jpg" width="300" height="185" alt="Lotus"> </a>
JavaScript では querySelector() などを使って任意のクラス名(この例の場合は luminous)を設定した a 要素(trigger element)を取得して、new Luminous() の引数に渡して初期化します。
そのページに対象の要素がない場合は、初期化を行わないようにするため、querySelector() の戻り値が null でない場合にのみ初期化するようにしています。
//luminous クラスを指定した a 要素を取得
const luminousElem = document.querySelector('.luminous');
//上記で要素が取得できていれば Luminous を初期化
if( luminousElem !== null ) {
new Luminous(luminousElem);
}
new Luminous() の第1引数には DOM 要素を指定する必要があります。querySelector() は一致する要素がない場合は null を返すので、一致する要素がない場合は new Luminous() に null が渡されて「Uncaught TypeError: `new Luminous` requires a DOM element as its first argument.」のようなエラーが発生します。
複数画像の単体表示
Luminous には複数の画像をライトボックス表示するためのギャラリー表示機能(LuminousGallery)がありますが、ギャラリー表示ではなく、複数の画像をそれぞれ単体表示するには、querySelectorAll() などを使って複数の a 要素(trigger element)をまとめて取得し、それぞれの要素に対して new Luminous() で初期化します。
以下の例では a 要素に luminous2 というクラスを指定しています。
<div class="col-md-4">
<a class="luminous2" href="../img/01-w1800.jpg">
<img src="../img/01-w600.jpg" alt="" width="300" height="185">
</a>
</div>
<div class="col-md-4">
<a class="luminous2" href="../img/02-w1800.jpg">
<img src="../img/02-w600.jpg" alt="" width="300" height="185">
</a>
</div>
<div class="col-md-4">
<a class="luminous2" href="../img/03-w1800.jpg">
<img src="../img/03-w600.jpg" alt="" width="300" height="185">
</a>
</div>
以下では querySelectorAll() で luminous2 クラスを指定した全ての a 要素を取得し、取得した要素の数が 0 より大きければ、NodeList のメソッド forEach() を使ってそれぞれの要素に対して new Luminous() で初期化しています。
//querySelectorAll() で全ての luminous2 クラスを指定した a 要素を取得
const luminousElems = document.querySelectorAll('.luminous2');
//取得した要素の数が 0 より大きければ
if( luminousElems.length > 0 ) {
luminousElems.forEach( (elem) => {
new Luminous(elem);
});
}
ギャラリー表示(LuminousGallery)
LuminousGallery を使うと、拡大表示した際に前後の画像へのナビゲーションを表示して、複数の画像にライトボックスを適用することができます。
以下の例では a 要素(trigger element)に luminous3 というクラスを指定しています。
<div class="col-md-4">
<a class="luminous3" href="../img/01-w1800.jpg">
<img src="../img/01-w600.jpg" alt="" width="300" height="185">
</a>
</div>
<div class="col-md-4">
<a class="luminous3" href="../img/02-w1800.jpg">
<img src="../img/02-w600.jpg" alt="" width="300" height="185">
</a>
</div>
<div class="col-md-4">
<a class="luminous3" href="../img/03-w1800.jpg">
<img src="../img/03-w600.jpg" alt="" width="300" height="185">
</a>
</div>
JavaScript では取得した要素の集まり(NodeList や HTMLCollection)を new LuminousGallery() に渡して初期化します。
//querySelectorAll() で全ての luminous3 クラスを指定した a 要素を取得
const luminousGalleryElems = document.querySelectorAll('.luminous3');
// または getElementsByClassName() を使ってもほぼ同じ(※)
//const luminousGalleryElems = document.getElementsByClassName('luminous3');
//取得した要素の数が 0 より大きければ
if( luminousGalleryElems.length > 0 ) {
// LuminousGallery で初期化
new LuminousGallery(luminousGalleryElems);
}
※ querySelectorAll() と getElementsByClassName()
document.querySelectorAll('.luminous3') と document.getElementsByClassName('luminous3') で取得される要素はいずれも luminous3 クラスが指定されている要素ですが、querySelectorAll() で取得されるのは静的な NodeList ですが、getElementsByClassName() で取得されるのは HTMLCollection で document が変更された時点で自動的に更新されるライブオブジェクトです。
そのため、フィルタリングなどでドキュメントを操作した場合、getElementsByClassName() で取得した HTMLCollection にはその変更が反映されますが、querySelectorAll() で取得した NodeList には反映されません。
単体&ギャラリー表示
単体表示とギャラリー表示で別々のクラスを設定して、それぞれについて初期化して使用することもできますが、場合によってはページにライトボックス表示する画像が1つの場合は単体表示で、複数ある場合はギャラリー表示にすることもできます。
以下は、 .luminous を指定した a 要素を querySelectorAll() で全て取得し、取得した要素が複数ある場合は LuminousGallery を使いギャラリー表示を適用し、1つだけの場合は Luminous を使って単体表示を適用する例です。
querySelectorAll() の戻り値は NodeList なので 1つだけの場合は new Luminous() の引数には luminousElems[0] のように先頭の DOM 要素を渡します。
//ページに .luminous が複数ある場合は、ギャラリー表示、1つだけの場合は単体表示する場合の例
const luminousElems = document.querySelectorAll('.luminous');
if( luminousElems !== null ) {
if( luminousElems.length > 1 ) {
//対象の要素が複数ある場合は LuminousGallery でギャラリー表示
new LuminousGallery(luminousElems);
}else if(luminousElems[0]){
//対象の要素が1つだけの場合 Luminous で単体表示
new Luminous(luminousElems[0]);
}
}
複数のギャラリー表示
1つのページ内で複数のグループに分けてギャラリー表示する例です。
以下の場合、.luminousX1、.luminousX2、.luminousX3、.luminousX4、.luminousX5、という5つの異なるクラスを指定することで、それぞれでグループごとにギャラリー表示することができます。
for(let i=1; i<6; i++) {
const luminousClass = '.luminousX' + i;
let luminousGalleries = document.querySelectorAll(luminousClass);
if( luminousGalleries.length > 0 ) {
new LuminousGallery(luminousGalleries);
}
}
以下はキャプション(次項)を設定する場合の例です。オプションは内容が同じでも個々に設定します。
for(let i=1; i<6; i++) {
//キャプション(オプション)
let luminousOptsX = 'luminousOptsX' + i;
luminousOptsX = {
caption: (trigger) => {
if(trigger.querySelector('img').hasAttribute('alt')) {
return trigger.querySelector('img').getAttribute('alt');
}else{
return '';
}
},
}
const luminousClass = '.luminousX' + i;
let luminousGalleries = document.querySelectorAll(luminousClass);
if( luminousGalleries.length > 0 ) {
//LuminousGallery の場合は、第3引数にオプションを指定
new LuminousGallery(luminousGalleries, {}, luminousOptsX);
}
}
キャプションの表示
キャプションを表示するには、初期化の際にオプションの caption を指定します。
※ 単体表示の new Luminous() では第2引数に、ギャラリー表示の new LuminousGallery() では第3引数にオプションの caption を指定します。
デフォルトではオプションの caption は null になっていますが、文字列または文字列を返す関数を指定することができます。
caption に関数を指定する場合、その関数は a 要素(trigger element)を引数として受け取ります。関数が返す文字列(キャプションとして出力される値)は HTML も記述できるため、ユーザーからの入力を利用する場合はエスケープするなど注意が必要です。
単体表示のキャプション
以下は画像の alt 属性の値をキャプションとする例です。
<a class="luminous4" href="../img/01-w1800.jpg"> <img src="../img/01-w600.jpg" width="300" height="185" alt="白い蓮の花"> </a>
caption: に指定する関数に渡される引数 trigger は a 要素(trigger element)です。
//オプションの設定
const luminousOpts = {
//alt 属性の値をキャプションに表示
caption: (trigger) => {
//img 要素に alt 属性が設定されていれば
if(trigger.querySelector('img').hasAttribute('alt')) {
//img 要素の alt 属性の値を取得して返す
return trigger.querySelector('img').getAttribute('alt');
}else{
return '';
}
},
}
const luminousElemCaption = document.querySelector('.luminous4');
if( luminousElemCaption !== null ) {
//第2引数にオプションを指定
new Luminous(luminousElemCaption, luminousOpts);
}
上記では、img 要素に alt 属性が設定されていることを確認してから alt 属性の値を取得して返していますが、img 要素には alt 属性は必須なのと、getAttribute() も指定した属性が存在しない場合はほとんどのブラウザは null ではなく、空文字列を返すようなので以下のように記述しても問題ないかと思います。
const luminousOpts = {
caption: (trigger) => {
return trigger.querySelector('img').getAttribute('alt');
},
}
a 要素の data-* 属性の値をキャプションに表示
以下は a 要素に data-caption 属性が設定されていて、その値が空でない場合はその値をキャプションとして表示する例です。
a 要素に data-caption 属性が設定されていない場合は img 要素の alt 属性の値をキャプションとして表示します。
<a class="luminous5" href="../img/01-w1800.jpg" data-caption="白い蓮の花(data-caption)"> <img src="../img/01-w600.jpg" width="300" height="185" alt="白い蓮の花の画像"> </a>
//オプションの設定
const luminousOpts2 = {
caption: (trigger) => {
//a 要素に data-caption 属性が設定されていて、その値が空でない場合
if(trigger.hasAttribute('data-caption') && trigger.getAttribute('data-caption') !== '') {
//a 要素の data-caption 属性の値をキャプションとして表示
return trigger.getAttribute('data-caption');
}else{
//a 要素の data-caption 属性が設定されていなければ img 要素の alt 属性の値を表示
return trigger.querySelector('img').getAttribute('alt');
}
},
}
const luminousElemCaption2 = document.querySelector('.luminous5');
if( luminousElemCaption2 !== null ) {
new Luminous(luminousElemCaption2, luminousOpts2);
}
figcaption の値をキャプションに表示
以下は figcaption の値をキャプションに表示する例です。figcaption が設定されていない場合や figcaption の値が空の場合は alt 属性の値をキャプションとして表示します。
figcaption 要素は、Node インターフェイスの parentElement プロパティを使って a 要素(trigger)の親要素を起点に querySelector() で取得しています。
<figure>
<a class="luminous6" href="../img/02-w1800.jpg">
<img src="../img/02-w600.jpg" alt="庭の石の上の猫の画像" width="300" height="185">
</a>
<figcaption>庭に来る半野良の猫(トラ)</figcaption>
</figure>
const luminousOpts3 = {
caption: (trigger) => {
//figcaption 要素を取得
const figCaption = trigger.parentElement.querySelector('figcaption');
if(figCaption !== null && figCaption.textContent !== '') {
return figCaption.textContent;
}else{
if(trigger.querySelector('img').hasAttribute('alt')) {
return trigger.querySelector('img').getAttribute('alt');
}else{
return '';
}
}
},
}
const luminousElemCaption3 = document.querySelector('.luminous6');
if( luminousElemCaption3 !== null ) {
new Luminous(luminousElemCaption3, luminousOpts3);
}
ギャラリー表示のキャプション
ギャラリー表示(LuminousGallery)の場合、new LuminousGallery() の第2引数にはギャラリーオプションを指定し、第3引数に caption のオプションを指定します。
この例では第2引数に指定するギャラリーオプションは特に指定しないので(あまり指定するオプションもないようなので)、第2引数には空のオブジェクト { } を指定しています。
その他は前述の caption のオプションと同じです。
// caption のオプション(img 要素の alt 属性の値をキャプションに表示)
const luminousOpts4 = {
caption: (trigger) => {
return trigger.querySelector('img').getAttribute('alt');
},
}
const luminousGalleryElems2 = document.querySelectorAll('.luminous7');
if( luminousGalleryElems2.length > 0 ) {
//第3引数に caption のオプションを指定
new LuminousGallery(luminousGalleryElems2, {}, luminousOpts4);
}
<div class="col-md-4">
<a class="luminous7" href="../img/01-w1800.jpg">
<img src="../img/01-w600.jpg" alt="白い蓮の花" width="300" height="185">
</a>
</div>
<div class="col-md-4">
<a class="luminous7" href="../img/02-w1800.jpg">
<img src="../img/02-w600.jpg" alt="庭に来る半野良の猫" width="300" height="185">
</a>
</div>
<div class="col-md-4">
<a class="luminous7" href="../img/03-w1800.jpg">
<img src="../img/03-w600.jpg" alt="枝垂れ桜と経堂" width="300" height="185">
</a>
</div>
画像枚数と現在の画像のインデックスの表示
以下はギャラリー表示でキャプションに画像の枚数と現在表示されている画像のインデックス(位置)を表示する例です。
ギャラリー表示の対象の画像の枚数は、a 要素(trigger element)に指定されているクラスを持つ全ての要素を取得してその長さを取得します。
以下の例では配列のメソッド indexOf() を使って現在の位置を取得するため、querySelectorAll() で取得した要素の集まり(NodeList)を配列に変換しています。
caption のコールバック関数の引数 trigger は現在表示されている画像のリンク要素(a 要素)なので、indexOf() に trigger を指定してインデックスを取得しています(配列のインデックスは0から始まるので1を加算)。
caption には HTML が使えるので、クラスを指定した span 要素で画像枚数とインデックスを囲んでスタイルを設定できます。
//caption のオプション
const luminousOptsFr = {
caption: (trigger) => {
//a 要素(trigger element)に指定されているクラスを持つ全ての要素を取得して配列に変換
const elemsArray = Array.prototype.slice.call( document.querySelectorAll('.' + trigger.className) ) ;
//画像の枚数
const total = elemsArray.length;
//現在の画像のインデックス(添字 + 1)
const index = elemsArray.indexOf( trigger ) + 1 ;
//画像の alt 属性の値及び画像枚数とインデックスをキャプションとする
return trigger.querySelector('img').getAttribute('alt') +
'<span class="fraction">' + index + '/' + total + '</span>';
},
}
const luminousGalleryElemsFr = document.querySelectorAll('.luminousFr');
if( luminousGalleryElemsFr.length > 0 ) {
new LuminousGallery(luminousGalleryElemsFr, {}, luminousOptsFr);
}
上記の場合、画像の alt 属性の値をキャプションとしていますが、画像枚数とインデックスだけを表示する場合は以下のようになります。また、以下の例では配列のようなオブジェクトを配列に変換するのに Array.from() を使っています。
const luminousOptsFr = {
caption: (trigger) => {
// この例では Array.from() を使って配列に変換
const elemsArray = Array.from(document.querySelectorAll('.' + trigger.className));
const total = elemsArray.length;
const index = elemsArray.indexOf( trigger ) + 1 ;
return index + '/' + total;
},
}
<div class="col-md-4">
<a class="luminousFr" href="../img/01-w1800.jpg">
<img src="../img/01-w600.jpg" alt="白い蓮の花" width="300" height="185">
</a>
</div>
<div class="col-md-4">
<a class="luminousFr" href="../img/02-w1800.jpg">
<img src="../img/02-w600.jpg" alt="庭に来る半野良の猫" width="300" height="185">
</a>
</div>
<div class="col-md-4">
<a class="luminousFr" href="../img/03-w1800.jpg">
<img src="../img/03-w600.jpg" alt="枝垂れ桜と経堂" width="300" height="185">
</a>
</div>
この例では画面幅が 460px より大きい場合は、画像枚数とインデックスは position: absolute と right: 0 で右端に表示し、画面幅が 460px 以下の場合はキャプションの右側に表示するようにしています。画面幅を 460px で切り替えているのは、luminous-basic.css のメディアクエリに合わせているためです。
span.fraction {
display: inline-block;
position: absolute;
right: 0;
font-size: 12px;
color: #bbb;
}
@media (max-width: 460px) {
span.fraction {
position: relative;
margin-left: 2rem;
}
/* 独自の設定 */
.lum-lightbox-inner img {
max-width: 240vw;
max-height: 90vh;
}
/* 独自の設定 */
.lum-lightbox-caption {
position: relative;
}
}
以下は left: 0; と top: 0; を指定して左上に表示する例です(画面幅や画像サイズによっては画像の上に表示されます)。
span.fraction {
display: inline-block;
position: absolute;
left: 0;
top: 0;
font-size: 12px;
color: #fff;
}
表示する span 要素(span.fraction)を絶対配置にした場合、その基準となる要素(position が static 以外の要素)は div.lum-lightbox-inner になります。
<div class="lum-lightbox lum-open">
<div class="lum-lightbox-inner"><!-- 基準となる要素 -->
<div class="lum-lightbox-loader"></div>
<div class="lum-lightbox-image-wrapper">
<span class="lum-lightbox-position-helper">
<img class="lum-img" src="../img/01-w1800.jpg">
<p class="lum-lightbox-caption">白い蓮の花<span class="fraction">1/3</span></p>
</span>
</div>
<button class="lum-previous-button lum-gallery-button">previous</button>
<button class="lum-next-button lum-gallery-button">next</button>
</div>
<div class="lum-close-button"></div>
</div>
.lum-lightbox-inner {
top: 2.5%;
right: 2.5%;
bottom: 2.5%;
left: 2.5%;
}
そのため、画面幅や画像サイズによって表示される位置が異なってくるので、キャプションの中に配置した方が無難かもしれません。
関連項目:何番目の要素か(インデックス)を取得
サンプル
以下はキャプションと画像枚数と現在の画像のインデックスを表示する例です。
1つのページ内で異なるクラス(lumi1, lumi2, lumi3, lumi4, lumi5)を指定することにより、複数のギャラリー表示ができます。以下のサンプルでは .lumi1 を指定しています。
また、キャプションは .caption を指定した要素があればそのテキスト、figcaption 要素があればそのテキスト、いずれもない場合は alt 属性の値を表示します。
<div class="row">
<div class="col-md-4">
<a class="lumi1" href="../img/01-w1800.jpg">
<img src="../img/01-w600.jpg" alt="白い蓮の花の写真" width="300" height="185">
</a>
<p class="caption">白い蓮の花(.caption)</p>
</div>
<div class="col-md-4">
<figure>
<a class="lumi1" href="../img/02-w1800.jpg">
<img src="../img/02-w600.jpg" alt="庭に来た半野良の猫の写真" width="300" height="185">
</a>
<figcaption>庭に来る半野良の猫(figcaption)</figcaption>
</figure>
</div>
<div class="col-md-4">
<a class="lumi1" href="../img/03-w1800.jpg">
<img src="../img/03-w600.jpg" alt="枝垂れ桜と経堂の写真(alt 属性)" width="300" height="185">
</a>
</div>
</div>
for(let i=1; i<6; i++) {
//キャプション(オプション)
let lumiOptsX = 'lumiOptsX' + i;
lumiOptsX = {
caption: (trigger) => {
//a 要素(trigger element)に指定されているクラスを持つ全ての要素を取得して配列に変換
const elemsArray = Array.prototype.slice.call( document.querySelectorAll('.' + trigger.className) ) ;
//画像の枚数
const total = elemsArray.length;
//現在の画像のインデックス(添字 + 1)
const index = elemsArray.indexOf( trigger ) + 1 ;
//.caption を指定した要素
const captionElem = trigger.parentElement.querySelector('.caption');
//figcaption 要素
const figCaption = trigger.parentElement.querySelector('figcaption');
//.caption を指定した要素、または figcaption 要素のテキストをキャプションに(いずれもない場合は alt 属性の値)
if(captionElem !== null && captionElem.textContent !== '') {
//画像枚数が1枚より大きければインデックスをキャプションに追加
if(total > 1) {
return captionElem.textContent + '<span class="fraction">' + index + ' / ' + total + '</span>';
}else{
return captionElem.textContent;
}
}else if(figCaption !== null && figCaption.textContent !== '') {
if(total > 1) {
return figCaption.textContent + '<span class="fraction">' + index + ' / ' + total + '</span>';
}else{
return figCaption.textContent;
}
}else{
if(trigger.querySelector('img').hasAttribute('alt')) {
if(total > 1) {
return trigger.querySelector('img').getAttribute('alt') + '<span class="fraction">' + index + ' / ' + total + '</span>';
}else{
return trigger.querySelector('img').getAttribute('alt');
}
}else{
if(total > 1) {
return index + ' / ' + total ;
}else{
return '';
}
}
}
},
}
//lumi1, lumi2, lumi3, lumi4, lumi5 クラス
const lumiClass = '.lumi' + i;
let lumiGalleries = document.querySelectorAll(lumiClass);
if( lumiGalleries.length > 0 ) {
new LuminousGallery(lumiGalleries, {}, lumiOptsX);
}
}
.lumi1 + .caption, .lumi1 + figcaption {
text-align: center;
color: #999;
opacity: 0;
transition: opacity 0.4s ease 0s;
}
.lumi1:hover + .caption, .lumi1:hover + figcaption {
opacity: 1;
}
.lumi1 img {
opacity: 1;
transition: opacity 0.4s ease 0s;
}
.lumi1:hover img {
opacity: .75;
}
span.fraction {
display: inline-block;
position: absolute;
right: 0;
font-size: 12px;
color: #bbb;
}
@media (max-width: 460px) {
span.fraction {
position: relative;
margin-left: 2rem;
}
}
画像のプリロード(先読み込み)
多数の画像があり、拡大表示する画像のサイズが大きい場合などでは大きな画像の読み込みに時間がかかる場合があります。
そのような場合、拡大表示する画像をプリロード(先読み込み)しておくと、画像がメモリ上にキャッシュされ、拡大画像の表示がスムーズになります。
JavaScript で画像をプリロードするには、img 要素を生成して、src 属性に画像のパスを指定します(生成した画像は読み込まれるだけで表示されません)。
以下は、img 要素にサムネイル画像を指定して、luminous クラスを指定した a 要素の href 属性に拡大表示する画像のパスを指定してライトボックス表示する例です。
<div class="grid-wrapper">
<div class="grid">
<div class="grid-item">
<div class="grid-content">
<a class="luminous" href="images/01.jpg">
<img class="thumb" src="images/thumbs/01.jpg" alt="sample image 01">
</a>
<h3 class="title">Title 1</h3>
<div class="desc">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
</div>
</div>
</div>
<div class="grid-item">
<div class="grid-content">
<a class="luminous" href="images/02.jpg">
<img class="thumb" src="images/thumbs/02.jpg" alt="sample image 02">
</a>
<h3 class="title">Title 2</h3>
<div class="desc">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
</div>
</div>
</div>
<div class="grid-item">
<div class="grid-content">
<a class="luminous" href="images/03.jpg">
<img class="thumb" src="images/thumbs/03.jpg" alt="sample image 03">
</a>
<h3 class="title">Title 3</h3>
<div class="desc">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
</div>
</div>
</div>
<div class="grid-item">
<div class="grid-content">
<a class="luminous" href="images/04.jpg">
<img class="thumb" src="images/thumbs/04.jpg" alt="sample image 04">
</a>
<h3 class="title">Title 4</h3>
<div class="desc">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>
</div>
</div>
</div>
</div>
</div>
<script src="luminous.min.js"></script>
以下が JavaScript で17〜22行目がプリロードの部分です。
拡大表示する画像をプリロードするには、luminous クラスを指定した a 要素の数だけ img 要素を生成して、a 要素の href 属性を img 要素の src 属性に指定します。
この例の場合、luminous クラスを指定した a 要素は、Luminous Lightbox の初期化(10行目)ですでに取得しているので、for 文で個々のリンク要素から href 属性の値を取得して、生成した img 要素の src 属性に指定しています。
<script>
//img 要素の alt 属性の値をキャプションとして表示するためのオプション
const luminousOpts = {
caption: (trigger) => {
return trigger.querySelector('img').getAttribute('alt');
},
}
//全ての luminous クラスを指定した a 要素を取得
const luminousGalleryElems = document.querySelectorAll('.luminous');
//取得した要素が1つ以上あれば Luminous Lightbox を初期化
if( luminousGalleryElems.length > 0 ) {
new LuminousGallery(luminousGalleryElems, {}, luminousOpts);
}
//画像の先読み
//拡大画像へのリンク要素は上記の luminousGalleryElems で取得されているのでそれを利用
for(let i=0; i<luminousGalleryElems.length; i++) {
// img 要素を生成
const img = document.createElement('img');
// img 要素の src 属性にリンクの href 属性の値を設定
img.src = luminousGalleryElems[i].href;
}
</script>
この例の場合、画像は4枚だけなので、プリロードしなくてもあまり変わりませんが、サイズの大きい画像が多数ある場合はプリロードしないとライトボックス表示する際に時間がかかる可能性があります。
ブラウザのインスペクタのネットワークで確認すると、拡大表示する画像がプリロードされているのが確認できます。
場合によっては load イベントでサムネイル画像を含む読み込みのすべてが完了してからプリロードする方が良いかもしれません。
window.addEventListener('load', () =>{
for(let i=0; i<luminousGalleryElems.length; i++) {
const img = document.createElement('img');
img.src = luminousGalleryElems[i].href;
}
});
また、img 要素が読み込まれた時点で行う処理があれば以下ように画像の load イベントで実行可能です(window の load イベントと合わせて使うこともできます)。
for(let i=0; i<luminousGalleryElems.length; i++) {
const img = document.createElement('img');
img.src = luminousGalleryElems[i].href;
// img 要素が読み込まれた時点で行う処理があれば以下ように画像の load イベントで実行可能
img.addEventListener('load', function(){
//画像の height 及び width プロパティの値をコンソールに出力
console.log(`height:${img.height} / width:${img.width}`);
});
}
この例では CSS Grid を使っています。
.grid-wrapper {
padding: 1.5em;
max-width: 1200px;
margin-right: auto;
margin-left: auto;
}
.grid {
display: grid;
grid-gap: 1rem;
gap: 1rem;
grid-template-columns: repeat( auto-fill, minmax( 240px, 1fr ) );
}
.grid-item {
background-color: #efefef;
}
.thumb {
max-width: 100%;
}
.title {
font-size: 16px;
color: #999;
text-align: center;
}
.desc {
padding: 0 1rem;
position: relative;
overflow: visible;
color: #aaa;
font-size: 14px;
}
前後の画像のみをプリロード(個人的なメモ)
前述の例では拡大(ライトボックス)表示する全ての画像をプリロードしましたが、以下はライトボックス表示した前後の画像のみをプリロードする例です(HTML と CSS は前述の例と同じ)。但し、ライトボックス表示した際に前後の画像をプリロードするので、最初にクリックしてライトボックス表示する画像はプリロードされていません。
また、ライトボックス表示される際のクラス名などを使用しているので、プラグイン自体の HTML(クラス名)などの変更があると機能しなくなります。
※ この方法が有効かどうかなどは検証できていません(個人的なメモのようなものです)。
ライトボックス表示される画像や前後の矢印ボタンはライトボックス表示されてからでないと取得できないのでオプションの onOpen: にライトボックス画像を表示する際に呼び出される関数を指定しています。
//img 要素の alt 属性の値をキャプションとして表示するためのオプション
const luminousOpts = {
caption: (trigger) => {
return trigger.querySelector('img').getAttribute('alt');
},
onOpen: () => {
//ライトボックス画像を表示する際に呼び出される関数
loadPrevNext();
},
onClose: () => {
//ライトボックス画像を閉じる際に呼び出される関数(不要?)
removeEvents();
}
}
//全ての luminous クラスを指定した a 要素を取得
const luminousGalleryElems = document.querySelectorAll('.luminous');
//取得した要素が1つ以上あれば Luminous Lightbox を初期化
if( luminousGalleryElems.length > 0 ) {
new LuminousGallery(luminousGalleryElems, {}, luminousOpts);
}
//全ての luminous クラスを指定した a 要素の配列を作成
const luminousGalleryElems_array = Array.prototype.slice.call( luminousGalleryElems ) ;
//上記配列の長さ
const elems_array_length = luminousGalleryElems.length;
//拡大表示する画像の src を格納する配列
const src_array = [];
//luminous クラスを指定した a 要素を for 文でループ
for(let i=0; i<elems_array_length; i++) {
//それぞれにクリックイベントを設定
luminousGalleryElems[i].addEventListener('click', () => {
//その要素が全体の何番目か(インデックス)を取得
const index = luminousGalleryElems_array.indexOf( luminousGalleryElems[i]) ;
//img 要素を生成
const nextImg = document.createElement('img');
//生成した img 要素にその要素の次の要素のパスを設定してプリロード
if(index === elems_array_length-1){
nextImg.src = luminousGalleryElems[0].href;
}else{
nextImg.src = luminousGalleryElems[i+1].href;
}
//生成した img 要素にその要素の前の要素のパスを設定してプリロード
const prevImg = document.createElement('img');
if(index === 0){
prevImg.src = luminousGalleryElems[elems_array_length-1].href;
}else{
prevImg.src = luminousGalleryElems[i-1].href;
}
});
//拡大表示する画像の src を格納する配列にその要素のパスを追加
src_array.push(luminousGalleryElems[i].href);
}
//現在表示されている拡大画像の前の画像をプリロードする関数
const loadPrev = () => {
//ライトボックス表示された画像を取得
const lumImage = document.querySelector('.lum-img');
//現在表示されている拡大画像が全体の何番目か(インデックス)を取得
const src_index = src_array.indexOf( lumImage.src) ;
//img 要素を生成
const prevImg = document.createElement('img');
//生成した img 要素に現在表示されている拡大画像の前の要素のパスを設定してプリロード
if(src_index === 0){
prevImg.src = src_array[src_array.length-1];
}else{
prevImg.src = src_array[src_index-1];
}
}
//現在表示されている拡大画像の次の画像をプリロードする関数
const loadNext = () => {
//ライトボックス表示された画像を取得
const lumImage = document.querySelector('.lum-img');
//現在表示されている拡大画像が全体の何番目か(インデックス)を取得
const src_index = src_array.indexOf( lumImage.src) ;
//img 要素を生成
const nextImg = document.createElement('img');
//生成した img 要素に現在表示されている拡大画像の次の要素のパスを設定してプリロード
if(src_index === src_array.length-1){
nextImg.src = src_array[0];
}else{
nextImg.src = src_array[src_index+1];
}
}
//ライトボックス画像を表示する際に呼び出される関数を定義
const loadPrevNext = () => {
//「前へ」のボタン要素
const prevButton = document.getElementsByClassName('lum-previous-button')[0];
//「次へ」のボタン要素
const nextButton = document.getElementsByClassName('lum-next-button')[0];
if(prevButton) {
//「前へ」のボタン要素にクリックイベントを設定
prevButton.addEventListener('click',loadPrev);
}
if(nextButton) {
//「次へ」のボタン要素にクリックイベントを設定
nextButton.addEventListener('click',loadNext);
}
}
//ライトボックス画像を閉じる際に呼び出される関数を定義
const removeEvents = () => {
//「前へ」のボタン要素
const prevButton = document.getElementsByClassName('lum-previous-button')[0];
//「次へ」のボタン要素
const nextButton = document.getElementsByClassName('lum-next-button')[0];
if(prevButton) {
//「前へ」のボタン要素のクリックイベントを削除
prevButton.removeEventListener('click',loadPrev);
}
if(nextButton) {
//「次へ」のボタン要素のクリックイベントを削除
nextButton.removeEventListener('click',loadNext);
}
}
options
以下は、Luminous に用意されているオプションと単体表示とギャラリー表示でのオプションの設定例です。ギャラリー表示(LuminousGallery)の場合は、第2引数はギャラリーオプションを指定し、第3引数にオプションを指定します。
ギャラリー表示(LuminousGallery)の場合、第2引数のギャラリーオプションを指定する必要がない場合は、空のオブジェクトを指定します。
また、オプションは生成する Luminous または LuminousGallery のインスタンスにそれぞれ作成して設定します(内容が同じだからと言って、共用すると期待通りの動作にならないようです)。
//デフォルトのオプション
var options = {
namespace: null,
sourceAttribute: "href",
caption: null, //キャプション
openTrigger: "click",
closeTrigger: "click",
closeWithEscape: true,
closeOnScroll: false,
showCloseButton: true, //ドキュメントでは false となっている
appendToNode: document.body,
appendToSelector: null,
onOpen: null,
onClose: null,
includeImgixJSClass: false,
injectBaseStyles: true
};
//単体表示の場合(第2引数にオプションを指定)
new Luminous(document.querySelector(".luminous"), options);
//ギャラリー表示の場合(第3引数にオプションを指定)
new LuminousGallery(document.querySelector(".luminous"), {}, options);
| オプション | 意味 | デフォルト値 |
|---|---|---|
| namespace | ライトボックス表示する際に生成される要素に付与されるクラス名のプレフィックス。例えば、'wdl' を指定すると外側の div 要素には wdl-lightbox や wdl-open クラスが追加されます。デフォルトのクラス(lum-lightbox や lum-open など)は常に付与されます。独自クラスの追加 | null |
| sourceAttribute | ライトボックス画像(拡大画像)のパスを指定する属性 | "href" |
| caption | キャプションに表示する文字列や文字列を返す関数。キャプションへの出力はエスケープされないのでユーザーの入力値を使用する場合は注意が必要です。 | null |
| openTrigger | ライトボックス画像を表示する際のイベント | "click" |
| closeTrigger | ライトボックス画像を閉じる際のイベント | "click" |
| closeWithEscape | esc キーを押すことでクローズさせるかどうか | true |
| closeOnScroll | スクロールでクローズさせるかどうか | false |
| showCloseButton | クローズ(閉じる)ボタンを表示するかどうか | true |
| appendToNode | ライトボックス画像を追加(append)するノード | document.body |
| appendToSelector | ライトボックス画像を追加(append)する要素(セレクタ)。appendToNode よりも優先されます。 | null |
| onOpen | ライトボックス画像を表示する際に呼び出される関数 | null |
| onClose | ライトボックス画像を閉じる際に呼び出される関数 | null |
| includeImgixJSClass | true を指定すると、ライトボックス内の img 要素に imgix-fluid と言うクラスを追加します。 | false |
| injectBaseStyles | 基本スタイルを追加するかどうか。追加されるスタイルは injectBaseStylesheet.js により挿入されます。 | true |
ソースコード
CSS スタイル
必要に応じて独自のスタイルシートで Luminous のスタイルを上書きします。
Luminous のスタイルは luminous-basic.css に記述されているので、それを元にスタイルを設定します。
以下はライトボックス画像が表示される際に </body> の直前に挿入される HTML の例です。
<div class="lum-lightbox lum-open">
<div class="lum-lightbox-inner">
<div class="lum-lightbox-loader"></div>
<div class="lum-lightbox-image-wrapper" style="width: 1603px; max-width: 1603px; height: 411px; max-height: 411px;">
<span class="lum-lightbox-position-helper">
<img class="lum-img" src="..img/01-w1800.jpg">
<p class="lum-lightbox-caption">白い蓮の花</p>
</span>
</div>
<button class="lum-previous-button lum-gallery-button">previous</button>
<button class="lum-next-button lum-gallery-button">next</button>
</div>
<div class="lum-close-button"></div>
</div>
luminous-basic.css には例えば以下のような記述があります。
.lum-lightbox {
/* ライトボックスの背景色 */
background: rgba(0, 0, 0, 0.6);
}
/* ライトボックス画像の表示領域 */
.lum-lightbox-inner {
top: 2.5%;
right: 2.5%;
bottom: 2.5%;
left: 2.5%;
}
.lum-lightbox-inner img {
position: relative;
}
/* キャプション(画面幅が460px以上の場合) */
.lum-lightbox-inner .lum-lightbox-caption {
margin: 0 auto;
color: #fff;
max-width: 700px;
text-align: center;
}
以下は独自のスタイルシートで、背景色を上書きし、z-index を追加する例です。
.lum-lightbox {
background: rgba(0, 0, 0, 0.7); /* 背景色を上書き */
z-index: 9999; /* z-index を追加 */
}
以下はクローズボタンのスタイルです。X 印は :before と :after で幅2px、高さ32px、背景色が白の疑似要素をそれぞれ45度回転させて作成されています。
.lum-close-button {
position: absolute;
right: 5px; /* ボタンの位置 */
top: 5px; /* ボタンの位置 */
width: 32px; /* ボタンの幅 */
height: 32px; /* ボタンの高さ */
opacity: 0.3;
}
.lum-close-button:hover {
opacity: 1;
}
.lum-close-button:before,
.lum-close-button:after {
position: absolute;
left: 15px;
content: " ";
height: 33px; /* X 印の高さ(長さ)*/
width: 2px; /* X 印の幅(太さ)*/
background-color: #fff;
}
.lum-close-button:before {
transform: rotate(45deg);
}
.lum-close-button:after {
transform: rotate(-45deg);
}
以下はギャラリー表示の際に表示される前後の画像への矢印ボタンのスタイルです。
.lum-previous-button {
left: 12px;
}
.lum-next-button {
right: 12px;
}
/* 左右の矢印ボタンの共通のスタイル */
.lum-gallery-button:after {
content: "";
display: block;
position: absolute;
top: 50%;
width: 36px;
height: 36px;
border-top: 4px solid rgba(255, 255, 255, 0.8);
}
.lum-previous-button:after {
transform: translateY(-50%) rotate(-45deg);
border-left: 4px solid rgba(255, 255, 255, 0.8);
box-shadow: -2px 0 rgba(0, 0, 0, 0.2);
left: 12%;
border-radius: 3px 0 0 0;
}
.lum-next-button:after {
transform: translateY(-50%) rotate(45deg);
border-right: 4px solid rgba(255, 255, 255, 0.8);
box-shadow: 2px 0 rgba(0, 0, 0, 0.2);
right: 12%;
border-radius: 0 3px 0 0;
}
メディアクエリ
メディアクエリは 460px 以下のものが1つだけ設定されているので必要に応じて追加します。以下は luminous-basic.css に記述されている画面幅が460px以下でのスタイルです。
img の max-width と max-height が none に設定され、画像のサイズそのままが表示されるようになっていて、大きな画像の場合はユーザーはスクロールして見ることができるようになっています。
@media (max-width: 460px) {
.lum-lightbox-image-wrapper {
display: flex;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
.lum-lightbox-caption {
width: 100%;
position: absolute;
bottom: 0;
}
.lum-lightbox-position-helper {
margin: auto;
}
/* 画像のサイズそのままが表示される */
.lum-lightbox-inner img {
max-width: none;
max-height: none;
}
}
以下は画面幅が460px以下でも画像が画面サイズ内に収まるように表示する例です(値は適当です)。
@media (max-width: 460px) {
.lum-lightbox-inner img {
max-width: 100vw;
max-height: 95vh;
}
}
以下はこのページの設定例です。
画面幅が460px以下の場合、画像の横幅は最大で画面幅の2.4倍、高さは最大 90vh、キャプションは position: relative にして、クローズボタンの存在がわかりやすいように背景色や opacity を変更しています。
@media (max-width: 460px) {
.lum-lightbox-inner img {
max-width: 240vw;
max-height: 90vh;
}
.lum-lightbox-caption {
position: relative;
}
.lum-close-button {
opacity: 0.7;
background: rgba(0,0,0,.8);
border-radius: 50%;
}
}
独自クラスの追加
全てのライトボックス表示のスタイルの変更は前述のように既存のスタイルを上書きすればよいのですが、複数のライトボックスのクラスを設定している場合で、特定のライトボックスに異なるスタイルを適用するにはオプションの namespace を設定して、独自のクラスを追加し、そのクラスに対してスタイルを設定することができます。
以下はオプションの namespace に 'blue' を設定する例です。
const luminousOpts5 = {
//オプションの namespace に 'blue' を設定
namespace: 'blue',
//キャプション
caption: (trigger) => {
return trigger.querySelector('img').getAttribute('alt');
},
}
//luminous8 クラスを指定した要素を取得
const luminousGalleryElems3 = document.querySelectorAll('.luminous8');
//取得した要素が1つ以上あればギャラリー表示のライトボックスを生成
if( luminousGalleryElems3.length > 0 ) {
//上記オプションを指定して初期化
new LuminousGallery(luminousGalleryElems3, {}, luminousOpts5);
}
ライトボックス表示する画像を囲んだ a 要素に luminous8 クラスを指定
<div class="col-md-4">
<a class="luminous8" href="../img/01-w1800.jpg">
<img src="../img/01-w600.jpg" alt="白い蓮の花" width="300" height="185">
</a>
</div>
<div class="col-md-4">
<a class="luminous8" href="../img/02-w1800.jpg">
<img src="../img/02-w600.jpg" alt="庭に来る半野良の猫" width="300" height="185">
</a>
</div>
<div class="col-md-4">
<a class="luminous8" href="../img/03-w1800.jpg">
<img src="../img/03-w600.jpg" alt="枝垂れ桜と経堂" width="300" height="185">
</a>
</div>
ライトボックス表示された画像には、namespace に設定した 'blue' を含むクラス(blue-lightbox や blue-lightbox-inner など)が追加されます。
全ての既存のクラス名の「lum」の部分を namespace に設定した文字列に置き換えたクラスが追加されます(既存のクラスはそのまま残ります)。
<div class="lum-lightbox blue-lightbox lum-open blue-open">
<div class="lum-lightbox-inner blue-lightbox-inner">
<div class="lum-lightbox-loader blue-lightbox-loader"></div>
<div class="lum-lightbox-image-wrapper blue-lightbox-image-wrapper" style="width: 357px; max-width: 357px; height: 752px; max-height: 752px;">
<span class="lum-lightbox-position-helper blue-lightbox-position-helper">
<img class="lum-img blue-img" src="..img/01-w1800.jpg">
<p class="lum-lightbox-caption blue-lightbox-caption">白い蓮の花</p>
</span>
</div>
<button class="lum-previous-button blue-previous-button lum-gallery-button blue-gallery-button">previous</button>
<button class="lum-next-button blue-next-button lum-gallery-button blue-gallery-button">next</button>
</div>
<div class="lum-close-button blue-close-button"></div>
</div>
例えば、以下のように追加されたクラスを使って、そのライトボックスのみにスタイルを適用することができます。
.blue-lightbox {
background: rgba(51,68,166,0.6); /* 背景色を変更 */
}
アイコンを表示
画像にマウスオーバーすると SVG 画像のアイコンを表示する例です。
以下が HTML です。アイコンは span 要素で表示します。
<div class="sample-content">
<a class="luminous9" href="../img/02-w1800.jpg">
<img class="thumb" src="../img/02-w600.jpg" alt="庭に来る半野良の猫">
</a>
<span class="icon-plus"></span>
</div>
アイコンは span 要素に ::after 疑似要素を使って表示します。初期状態では opacity:0 で非表示にしておいてホバー時に表示するようにし、絶対配置で水平及び垂直方向に中央配置するようにしています。
関連項目:CSS で svg 要素を表示
.sample-content {
position: relative;
overflow: hidden;
max-width: 280px;
}
.thumb {
max-width: 100%;
transition: transform 0.3s;
}
/*画像ホバー時に画像を拡大*/
.thumb:hover {
transform: scale(1.1);
}
/*ホバー時に画像の上に表示するアイコン*/
.icon-plus {
opacity: 0;
position: absolute;
/*アイコンホバー時に反応しないように*/
pointer-events: none;
/* トランジションで opacity を使って表示 */
transition: opacity 0.3s;
/* 以下は水平及び垂直方向に中央配置するための設定 */
top: 50%;
left: 50%;
margin-right: -50%;
transform: translate(-50%, -50%);
}
/*SVG アイコンを擬似要素 ::after で表示*/
.icon-plus::after{
display: inline-block;
margin: 0;
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='white' class='bi bi-plus-circle' viewBox='0 0 16 16'%3E %3Cpath d='M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z'/%3E %3Cpath d='M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z'/%3E%3C/svg%3E");
}
/*ホバー時にアイコンを表示*/
.sample-content:hover .icon-plus {
opacity: 1;
}
//オプションの設定
const luminousOpts9 = {
caption: (trigger) => {
//alt 属性の値をキャプションに表示
return trigger.querySelector('img').getAttribute('alt');
},
}
//luminous クラスを指定した a 要素を取得
const luminousElem9 = document.querySelector('.luminous9');
//上記で要素が取得できていれば Luminous を初期化
if( luminousElem9 !== null ) {
new Luminous(luminousElem9, luminousOpts9);
}
以下はホバー時にキャプションも表示する例です。
キャプションは caption クラスの div 要素内の p 要素に記述しています。
<div class="grid">
<div class="grid-item">
<div class="grid-content">
<a class="luminous10" href="../img/01-w1800.jpg">
<img class="thumb" src="../img/01-w600.jpg" alt="白い蓮の花">
</a>
<span class="icon-plus"></span>
<div class="caption">
<p>白い蓮の花</p>
</div>
</div>
</div>
<div class="grid-item">
<div class="grid-content">
<a class="luminous10" href="../img/02-w1800.jpg">
<img class="thumb" src="../img/02-w600.jpg" alt="庭に来る半野良の猫">
</a>
<span class="icon-plus"></span>
<div class="caption">
<div class="caption">
<p>庭に来る半野良の猫</p>
</div>
</div>
</div>
</div>
<div class="grid-item">
<div class="grid-content">
<a class="luminous10" href="../img/03-w1800.jpg">
<img class="thumb" src="../img/03-w600.jpg" alt="枝垂れ桜と経堂">
</a>
<span class="icon-plus"></span>
<div class="caption">
<div class="caption">
<p>枝垂れ桜と経堂</p>
</div>
</div>
</div>
</div>
</div>
この例では CSS Grid を使って画像やキャプションを配置しています。
caption クラスの div 要素は初期状態では、transform: translateY(3rem) で下の方に配置して非表示にし(親要素に overflow: hidden を指定)、ホバー時に translateY(0px) で通常の位置に戻しています。
.grid {
display: grid;
grid-gap: 1rem;
gap: 1rem;
grid-template-columns: repeat( auto-fill, minmax( 240px, 1fr ) );
}
.grid-item {
}
.grid-content {
/*.caption の基準となるように position: relative を指定 */
position: relative;
/*キャプションが隠れるように*/
overflow: hidden;
}
.thumb {
max-width: 100%;
transition: transform 0.3s;
}
/*画像ホバー時に画像を拡大*/
.thumb:hover {
transform: scale(1.1);
}
.caption {
position: absolute; /*絶対配置*/
bottom: 0; /*基準をボトムに配置*/
text-align: center;
width: 100%;
height: 3rem;
background-color: rgba(0,0,0,0.55);
transform: translateY(3rem); /*3rem 下方向に配置*/
transition: transform 0.3s; /*アニメーション表示*/
font-size: .875rem;
pointer-events: none;
}
.caption p {
color: #fff;
margin-top: 1rem;
}
/*グリッドアイテムホバー時にキャプションを表示*/
.grid-item:hover .caption {
transform: translateY(0px);
}
/*ホバー時に画像の上に表示するアイコン*/
.icon-plus {
opacity: 0;
position: absolute;
transition: opacity 0.3s;
top: 50%;
left: 50%;
margin-right: -50%;
transform: translate(-50%, -50%);
pointer-events: none;
}
.grid-item:hover .icon-plus {
opacity: 1;
}
.icon-plus::after{
display: inline-block;
margin: 0;
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' fill='white' class='bi bi-plus-circle' viewBox='0 0 16 16'%3E %3Cpath d='M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z'/%3E %3Cpath d='M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z'/%3E%3C/svg%3E");
}
// caption のオプション(img 要素の alt 属性の値をキャプションに表示)
const luminousOpts10 = {
caption: (trigger) => {
return trigger.querySelector('img').getAttribute('alt');
},
}
//全ての luminous10 クラスを指定した a 要素を取得
const luminousGalleryElems10 = document.querySelectorAll('.luminous10');
//取得した要素が存在していれば
if( luminousGalleryElems10.length > 0 ) {
//キャプションのオプションを指定して LuminousGallery で初期化
new LuminousGallery(luminousGalleryElems10, {}, luminousOpts10);
}
関連ページ:CSS3 アニメーション SVG アイコンを表示
アイコンをクリックして表示
以下はホバー時に表示されるアイコンをクリックしてライトボックス表示する例です。
HTML では画像ではなくアイコンの要素を任意のクラス名(この例では luminous9x)を指定した a 要素で囲み、a 要素の href 属性に拡大画像のパスを指定します。
<div class="sample-content2">
<img class="thumb" src="../img/01-w600.jpg" alt="白い蓮の花">
<a class="luminous9x" href="../img/01-w1800.jpg">
<span class="icon-plus"></span>
</a>
</div>
この例では画像ホバー時に画像を暗く表示するように、画像の親要素の背景色に黒を指定して、画像ホバー時に画像を半透明(14行目)にしています。
前述の例ではアイコンに pointer-events: none を指定してアイコンホバー時に反応しないようにしていましたが、この例ではクリックできるように pointer-events は省略するか auto を指定します。
また、スマホなどでタッチしやすいようにアイコンには padding でタッチする範囲を広げています。
.sample-content2 {
position: relative;
overflow: hidden;
max-width: 280px;
/*画像ホバー時に暗くするように暗い背景色を指定*/
background-color: #000;
}
.thumb2 {
max-width: 100%;
transition: opacity 0.3s;
}
/*画像ホバー時に画像を半透明にして暗くする*/
.thumb2:hover {
opacity: 0.7;
}
/*ホバー時に画像の上に表示するアイコン*/
.icon-plus2 {
opacity: 0;
position: absolute;
transition: opacity .5s;
top: 50%;
left: 50%;
margin-right: -50%;
transform: translate(-50%, -50%);
/*以下はデフォルトなので省略可能*/
pointer-events: auto;
}
.sample-content2:hover .icon-plus2 {
opacity: 1;
}
.icon-plus2::after {
display: inline-block;
margin: 0;
/*タッチしやすいようにタッチする範囲を広げる*/
padding: 20px;
content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='30' height='30' fill='white' class='bi bi-plus-circle' viewBox='0 0 16 16'%3E %3Cpath d='M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z'/%3E %3Cpath d='M8 4a.5.5 0 0 1 .5.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3A.5.5 0 0 1 8 4z'/%3E%3C/svg%3E");
}
Luminous の初期化は通常の初期化同様、拡大画像の href 属性と任意のクラス名(この例では luminous9x)を指定した a 要素を指定します。
キャプションの指定では、trigger は a 要素なのでその親要素を parentElement で取得してそこから getElementsByTagName() で img 要素を取得しています。
const luminousOpts9x = {
caption: (trigger) => {
//画像要素の alt 属性を指定
return trigger.parentElement.getElementsByTagName('img')[0].getAttribute('alt');
},
}
const luminousElem9x = document.querySelector('.luminous9x');
if( luminousElem9x !== null ) {
new Luminous(luminousElem9x, luminousOpts9x);
}
WordPress で表示
以下は WordPress で Luminous を使ってライトボックス表示する例です。
この例ではテーマのディレクトリ内に「luminous」というディレクトリを作成し、その中にLuminous 関連のファイルを配置しています。
wp-content
└── themes
├── index.php
└── my-theme //使用するテーマのディレクトリ
├── content.php
├── footer.php
├── functions.php
├── header.php
├── index.php
├── luminous //Luminous 関連のファイルを格納するフォルダ
│ ├── luminous-basic.min.css //ダウンロードしたファイル
│ ├── luminous.min.js //ダウンロードしたファイル
│ └── myLuminous.js //初期化などを記述したファイル
├── sidebar.php
├── single.php
└── style.css
以下は Luminous の初期化を記述したファイル myLuminous.js です。
ライトボックス表示用のクラスを2つ用意しています。
- .luminous:ページに対象の要素が複数ある場合は LuminousGallery でギャラリー表示し、1つだけの場合 Luminous で単体表示
- .lumi-solo:ページに対象の要素が複数ある場合でも常に単体表示
また、キャプションはどちらの場合も figcaption 要素に値が記述されていれば、その値を表示し、figcaption 要素がなければ img 要素の alt 属性の値を表示します。
※オプションの設定内容は全く同じですが、それぞれ個別に設定します。
//オプション(キャプション)の設定
const luminousOpts = {
caption: (trigger) => {
const figCaption = trigger.parentElement.querySelector('figcaption');
if(figCaption !== null && figCaption.textContent !== '') {
return figCaption.textContent;
}else{
if(trigger.querySelector('img').hasAttribute('alt')) {
return trigger.querySelector('img').getAttribute('alt');
}else{
return '';
}
}
},
}
//luminous クラス(単体&ギャラリー表示用)を指定した a 要素を取得
const luminousElems = document.querySelectorAll('.luminous');
if( luminousElems !== null ) {
if( luminousElems.length > 1 ) {
//対象の要素が複数ある場合は LuminousGallery でギャラリー表示
new LuminousGallery(luminousElems, {}, luminousOpts);
}else if(luminousElems[0]){
//対象の要素が1つだけの場合 Luminous で単体表示
new Luminous(luminousElems[0], luminousOpts);
}
}
//アイキャッチ画像用オプション(キャプション)の設定
const lumiSoloOpts = {
caption: (trigger) => {
const figCaption = trigger.parentElement.querySelector('figcaption');
if(figCaption !== null && figCaption.textContent !== '') {
return figCaption.textContent;
}else{
if(trigger.querySelector('img').hasAttribute('alt')) {
return trigger.querySelector('img').getAttribute('alt');
}else{
return '';
}
}
},
}
//lumi-solo クラス(アイキャッチ画像用:ページに1つのみ)を指定した a 要素を取得
const lumiSoloElem = document.querySelector('.lumi-solo');
//上記で要素が取得できていれば Luminous を初期化
if( lumiSoloElem !== null ) {
new Luminous(lumiSoloElem, lumiSoloOpts);
}
functions.php
functions.php ではダウンロードした Luminous の CSS と JavaScript、及び上記のファイルを読み込みます。
function add_my_styles_and_scripts() {
・・・その他の読み込み・・
//Luminous の JavaScript の読み込み
wp_enqueue_script(
'luminous-js',
get_theme_file_uri( '/luminous/luminous.min.js' ),
array(), //依存するスクリプトはなし
filemtime( get_theme_file_path( '/luminous/luminous.min.js' ) ),
true //</body> 終了タグの前に配置
);
//Luminous の設定(初期化を記述したファイル)の読み込み
wp_enqueue_script(
'my-luminous',
get_theme_file_uri( '/luminous/myLuminous.js' ),
array('luminous-js'), //依存するスクリプト(Luminous の JavaScript)
filemtime( get_theme_file_path( '/luminous/myLuminous.js' ) ),
true //</body> 終了タグの前に配置
);
//CSS の読み込み
wp_enqueue_style(
'luminous-style',
get_theme_file_uri( '/luminous/luminous-basic.min.css' ),
array(), //依存するスタイルシートはなし
filemtime( get_theme_file_path( '/luminous/luminous-basic.min.css' ) )
);
}
add_action( 'wp_enqueue_scripts', 'add_my_styles_and_scripts' );
投稿に挿入した画像の表示
投稿に挿入した画像をライトボックス表示するには、投稿の編集画面でライトボックス表示する画像の設定でリンク先を「メディアファイル」にして「リンクCSSクラス」に myLuminous.js で設定したクラス「luminous」または「lumi-solo」を指定します。
キャプションを表示するには「Altテキスト」またはその画像のキャプションを設定します。
アイキャッチ画像の表示
アイキャッチ画像をライトボックス表示するには、テンプレートファイル(single.php など)でアイキャッチ画像を出力する際に拡大表示する画像へのリンクにクラス(以下の例では .lumi-solo)を指定します。
以下は投稿の個別ページで、挿入されたアイキャッチ画像があればライトボックスを適用する例です。
<?php if(have_posts()) : ?>
<?php while(have_posts()) : the_post(); ?>
・・・中略・・・
<?php if(has_post_thumbnail()) : ?>
<?php
$post_thumbnail_id = get_post_thumbnail_id();
$my_thumbnail = get_post( $post_thumbnail_id );
$my_thumbnail_caption = esc_html( $my_thumbnail->post_excerpt ); //キャプション
$src_info = wp_get_attachment_image_src( $post_thumbnail_id, 'large' );
$width = $src_info[ 1 ];
$height = $src_info[ 2 ];
?>
<figure>
<!-- a 要素にライトボックス用のクラスを指定し、href に拡大画像の URL(パス)を指定 -->
<a class="lumi-solo" href="<?php echo esc_url(get_the_post_thumbnail_url(get_the_ID(),'full')); ?>">
<img src="<?php echo esc_url(get_the_post_thumbnail_url(get_the_ID(),'large')); ?>" alt="<?php echo trim( strip_tags( get_post_meta( get_post_thumbnail_id($post->ID), '_wp_attachment_image_alt', true ) ) ) ?>" width="<?php echo $width; ?>" height="<?php echo $height; ?>">
</a>
<figcaption><?php echo $my_thumbnail_caption ? $my_thumbnail_caption : '' ?></figcaption>
</figure>
<?php endif; ?>
・・・中略・・・
<?php endwhile; ?>
<?php endif; ?>
関連ページ:アイキャッチ画像の出力
投稿に挿入した全ての画像に適用
以下は投稿に挿入された画像でリンク先を「メディアファイル」に設定している全ての画像を、記事の本文中の画像へのリンクを書き換えてライトボックス表示できるようにする例です。
リンクの書き換えは preg_replace_callback() を使っています。
この方法の場合、毎回画像にクラスを指定しなくてすむのと、すでに挿入されている画像もライトボックス表示の対象にできます。また、画像のリンク先を「メディアファイル」に設定していない画像はライトボックスは適用されません。
投稿のコンテンツを出力する the_content() の代わりに以下を記述します。
<?php
$content = get_the_content();
// /wp-content/uploads(アップロードした画像)へのリンクを表すパターン
$pattern = '/(<a )((class=")([^">]*)("))?([^>]*"http(s)?:[^">]*\/wp-content\/uploads\/[^">]*"[^>]*>)/u';
//preg_replace_callback() のコールバック関数
function add_luminous_class($matches) {
if ($matches[2] !== '' && $matches[3] !== '') {
//a 要素にクラスが指定されている場合は、そのクラスに続けてライトボックス用のクラスを追加
return $matches[1]. $matches[3] .$matches[4]. ' luminous' .$matches[5] .$matches[6];
} else {
//a 要素にクラスが指定されていなければ、ライトボックス用のクラス属性を追加
return $matches[1]. ' class="luminous"' .$matches[5] .$matches[6];
}
}
$content = preg_replace_callback($pattern, 'add_luminous_class', $content);
//the_content フィルターを適用
$content = apply_filters('the_content',$content);
$content = str_replace( ']]>', ']]>', $content );
echo $content;
?>

