pointer-eventsで画像ギャラリーやLightbox

画像表示でよくあるフェードして切り替えとかLightboxっぽい全画面表示とかもpointer-eventsプロパティと:target擬似要素の組み合わせで「っぽいもの」なら簡単に出来ます。最近はCSSで出来そうだなーと思うと、普通に出来るようになってて幸せですね!

ギャラリー

Demo: Image Gallery by pointer-events

まずは画像を並べるHTMLから。

<ol>
  <li id="img1"><a href="#img2"><img src="img1.png"></a></li>
  <li id="img2"><a href="#img3"><img src="img2.png"></a></li>
  <li id="img3"><a href="#img4"><img src="img3.png"></a></li>
  <li id="img4"><a href="#img5"><img src="img4.png"></a></li>
  <li id="img5"><a href="#img1"><img src="img5.png"></a></li>
</ol>

ごく普通の序列付きリストです。各画像には次の画像へのリンクが張ってあり、クリックで次の画像に進むというのをフェードインでの切り替えにするのが目標になります。

CSSもそれほどややこしいわけではなく、ほとんとモーダル・ウィンドウと同じです。pointer-eventsプロパティで操作不能に、opacityプロパティで隠しておいて、:target擬似要素で両者を復活させるというわけです。本当に同じですね! 画像のマークアップにli/a/imgと色々HTML要素を使っているのでdisplayプロパティでtableを利用する方法にしました(displayプロパティでtableを指定すると「表ですよ~」と読みあげてしまう読み上げブラウザがあるらしいのでご利用は計画的に)。

ol li {
  display: table;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9999;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  opacity: 0;
  transition-duration: 1.5s;
  pointer-events: none;
}

ol li:target {
  opacity: 1;
  pointer-events: auto;
  transition-duration: 3s;
}

ol li a {
  display: table-cell;
  width: 320px;
  height: 180px;
  vertical-align: middle;
  text-align: center;
}

理論上は何千枚でもCSSはこのままでいけるはずです。またここでは普通にフェードイン(アウト)させてるだけですが、widthプロパティを0にしておいたりすればスライドっぽい動きとかにもなるんじゃないかなーとか思います。ここらへんはCSSの工夫(主にTransition系の工夫)だけでいろいろ出来ると思います。

Lightbox

Demo: Lightbox by pointer-events

全画面で画像をズームするLightboxも、「っぽいもの」程度なら簡単にいけます。デモは新しいタブやウィンドウで開かないとうまく動かないので注意してください。

HTMLはちょっとアレです。閉じるためだけにonclick属性でJavaScriptを発火させざるを得ない感じでした。

<figure onclick="javascript:history.back()" id="img1">
  <a href="#img1"><img src="http://lorempixel.com/320/180/"></a>
</figure>

ここらへんが「CSSで~」とかキャッチーなタイトルがつかなかった理由ですね!

Lightboxはまずサムネイル画像を表示させておいて、クリックで全画面に、更にクリックで元に戻るという感じのUIなので、普通に画像を格納している要素のpointer-eventsを切り替えるだけではダメです。うまく動かすためには格納している要素(ここではfigure要素)とリンクになるa要素のpointer-eventsプロパティを:targetプロパティでスワップさせるようにします。

figure {
  pointer-events: none;
}

figure a {
  pointer-events: auto;
}

figure:target {
  background-color: rgba(0, 0, 0, 0.8);
  pointer-events: auto;
}

figure:target a {
  pointer-events: none;
}

figure:target a img {
  width: 80%;
  height: auto;
}

あとはほとんどギャラリーと同じですが、全画面の時に背景を暗くしてた上で幅に合わせてリサイズしてやるとらしくなります(heightプロパティをautoにすれば縦横比は維持されます)。

更にHTMLが汚くなりますが、背景画像として大きいサイズの画像を持っておけば全画面時に大きいサイズに切り替えるとかも可能です。

<figure onclick="javascript:history.back()" id="img1">
  <a href="#img1"
    style="background-image: url('http://lorempixel.com/640/360/nightlife/4/');">
    <img src="http://lorempixel.com/320/180/nightlife/4/">
  </a>
</figure>

全画面表示の時は右クリックから画像が保存できないというオプション付きです! こういうのは将来的にはHTMLのdata-*属性と超強化されるCSSのattr()関数でゴニョゴニョできるようになるはずなので期待して待ってましょう。


世間にはいろいろ事情があるのでpointer-eventsプロパティのブームはまだ来ないでしょうね……。

追記

history.back():targetでの状態の切り替えを組み合わせると、どうやらpointer-eventsの有効化の方がクリックイベントの終了より先に起こってしまうようです(多分)。なので履歴がある場合、Lightboxが開かれた直後にhistory.back()が発動してしまい、うまくLightboxが表示されません。ということでhistory.back()の代わりにlocation.hash='#close'するようにしたデモも作ってみました。

CSSだけでできないかなーと色々考えていますが、a要素のネストとか文法的にも実装的にもできない(WebKitでは閉じタグが補完される)のでなかなか難しそうな感じですね。img要素を2つもたせたりすれば出来るかもしれないですが、そんなHTML書きたくないし……。