クリックで画像を拡大するプラグイン(Lightbox)をjQueryなしで自作してみる

Lightboxという単語は死語感が漂っており、今どき需要もなさそうですが…。

というのはさておき、このブログで画像拡大プラグインを導入するにあたり、Photoswipeが候補に挙がったのですが、HTMLの構造の条件が厳しいのと、お手軽感がないのとで、結局自前で実装するに至りました。

デモ

下の写真をクリック/タップしてみてください。モーダルウインドウが開いて画像が拡大表示されます。モーダルウインドウは、ウインドウ内をクリック/タップ、スクロールで閉じることができます。

仕様

  • imgタグのsrc属性に指定されている画像を原寸大、もしくは画面いっぱいまで拡大します。HTMLに特別な構造、セレクタは必要ありません。
  • 画像を拡大した際にaltに指定した代替テキストを左上に表示します。
  • アニメーションは全てCSSに押し付けているので、柔軟にカスタマイズできます。
  • IEは全バージョン対応していません(縦横比がおかしくなる or 動かない)。ご了承ください。

コード

CSS

Stylusで書いたものと、コンパイル済みのものを置いておきます。お好きな方をお使いください。

#pixel-viewer
    display flex
    flex-direction column
    width 100%
    height 100%
    background rgba(0, 0, 0, .9)
    position fixed
    top 0
    left 0
    z-index 10
    animation fadein .2s ease-out both
    cursor pointer
    user-select none
    -webkit-tap-highlight-color transparent
    tap-highlight-color transparent

    &.fadeout
        animation fadeout .2s ease-out both
        pointer-events none /* ← IE救済措置(IEでこれがないと画像を閉じた後にページ全体がクリックできなくなります...)*/

        & #pixel-viewer__img
            animation scaledown .2s ease-out both

#pixel-viewer__alt
    width: 100%
    height 44px
    line-height 44px
    padding 0 48px 0 12px
    background rgba(0, 0, 0, .95)
    color #eee
    font-size 14px
    text-overflow ellipsis /* スマホなどでaltが横幅をはみ出たときに省略します */
    white-space nowrap
    overflow hidden

    &::before
    &::after
        content ""
        dispaly block
        width 24px
        height 3px
        background #eee
        border-radius 4px
        position fixed
        top 20px
        right 12px
        z-index 12
    &::before
        transform rotate(45deg)
    &::after
        transform rotate(-45deg)

#pixel-viewer__img
    display block
    margin auto
    max-width 100%
    max-height calc(100% - 44px)
    animation scaleup .2s ease-out both

@keyframes fadein
    0%
        opacity 0
    100%
        opacity 1

@keyframes scaleup
    0%
        transform scale(.95)
    100%
        transform scale(1)

@keyframes fadeout
    0%
        opacity 1
    100%
        opacity 0

@keyframes scaledown
    0%
        transform scale(1)
    100%
        transform scale(.95)
#pixel-viewer {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  background: rgba(0,0,0,0.9);
  position: fixed;
  top: 0;
  left: 0;
  z-index: 10;
  animation: fadein 0.2s ease-out both;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  tap-highlight-color: transparent;
}
#pixel-viewer.fadeout {
  animation: fadeout 0.2s ease-out both;
  pointer-events: none /* ← IE救済措置(IEでこれがないと画像を閉じた後にページ全体がクリックできなくなりますw)*/;
}
#pixel-viewer.fadeout #pixel-viewer__img {
  animation: scaledown 0.2s ease-out both;
}
#pixel-viewer__alt {
  width: 100%;
  height: 44px;
  line-height: 44px;
  padding: 0 48px 0 12px;
  background: rgba(0,0,0,0.95);
  color: #eee;
  font-size: 14px;
  text-overflow: ellipsis /* スマホなどでaltが横幅をはみ出たときに省略します */;
  white-space: nowrap;
  overflow: hidden;
}
/* 閉じるボタン */
#pixel-viewer__alt::before,
#pixel-viewer__alt::after {
  content: "";
  dispaly: block;
  width: 24px;
  height: 3px;
  background: #eee;
  border-radius: 4px;
  position: fixed;
  top: 20px;
  right: 12px;
  z-index: 12;
}
#pixel-viewer__alt::before {
  transform: rotate(45deg);
}
#pixel-viewer__alt::after {
  transform: rotate(-45deg);
}
#pixel-viewer__img {
  display: block;
  margin: auto;
  max-width: 100%;
  max-height: calc(100% - 44px);
  animation: scaleup 0.2s ease-out both;
}
@keyframes fadein {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}
@keyframes scaleup {
  0% {
    transform: scale(0.95);
  }
  100% {
    transform: scale(1);
  }
}
@keyframes fadeout {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}
@keyframes scaledown {
  0% {
    transform: scale(1);
  }
  100% {
    transform: scale(0.95);
  }
}

JS

function pixelViewer(element){
    const img = document.querySelectorAll(element);

    for(let i=0; i < img.length; i++){
        img[i].addEventListener('click', open);
    }

    function open(){
        const filter = document.createElement('div');
        filter.id = 'pixel-viewer';

        const div_alt = document.createElement('div');
        div_alt.id = 'pixel-viewer__alt';
        div_alt.textContent = this.alt;

        const div_img = document.createElement('img');
        div_img.id = 'pixel-viewer__img';
        div_img.src = this.src;

        document.body.appendChild(filter);
        filter.appendChild(div_alt);
        filter.appendChild(div_img);

        filter.addEventListener('click', close, {once: true});
        window.addEventListener('scroll', close, {once: true}); // スクロールで閉じたくない場合はこの行を削除

        function close(){
            filter.className = 'fadeout';
            filter.addEventListener("animationend",function(){
                filter.remove();
            });
        }
    }
}
pixelViewer('p > img');

引数に、記事内のimgタグを全て取得できるようなセレクタを指定してください。querySelectorAllなのでCSSと同じ感覚で書いて大丈夫です。

aタグの中に入った画像は対応していません。子セレクタなどを使って、リンクの中の画像を拾わないようにうまく指定してください。

HTML(記事内)

<p><img src="/post/only-css-popup-image/suisen.jpg" alt="スイセン"></p>

上のような感じで、基本的に何もしなくて大丈夫です。

alt表示なし版

左上のalt表示がないバージョンです。

function pixelViewer(element){
  const img = document.querySelectorAll(element);

  for(let i=0; i < img.length; i++){
    img[i].addEventListener('click', open);
  }

  function open(){
    const filter = document.createElement('div');
    filter.id = 'pixel-viewer';

    const div_img = document.createElement('img');
    div_img.id = 'pixel-viewer__img';
    div_img.src = this.src;

    document.body.appendChild(filter);
    filter.appendChild(div_img);

    filter.addEventListener('click', close, {once: true});
    window.addEventListener('scroll', close, {once: true}); // スクロールで閉じたくない場合はこの行を削除

    function close(){
      filter.className = 'fadeout';
      filter.addEventListener("animationend",function(){
          filter.remove();
      });
    }
  }
}
pixelViewer('p > img');
#pixel-viewer
    display flex
    flex-direction column
    width 100%
    height 100%
    background rgba(0,0,0,0.9)
    position fixed
    top 0
    left 0
    z-index 10
    animation fadein .2s ease-out both
    cursor pointer
    user-select none
    -webkit-tap-highlight-color transparent
    tap-highlight-color transparent

    &.fadeout
        animation fadeout .2s ease-out both
        pointer-events none

        & #pixel-viewer__img
            animation scaledown .2s ease-out both


    &::before
    &::after
        content ""
        dispaly block
        width 24px
        height 3px
        background #eee
        border-radius 4px
        position fixed
        top 20px
        right 12px
        z-index 12
    &::before
        transform rotate(45deg)
    &::after
        transform rotate(-45deg)

#pixel-viewer__img
    display block
    margin auto
    max-width 100%
    max-height calc(100% - 44px)
    animation scaleup .2s ease-out both

@keyframes fadein
    0%
        opacity 0
    100%
        opacity 1

@keyframes scaleup
    0%
        transform scale(.97)
    100%
        transform scale(1)

@keyframes fadeout
    0%
        opacity 1
    100%
        opacity 0

@keyframes scaledown
    0%
        transform scale(1)
    100%
        transform scale(.97)
#pixel-viewer {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  background: rgba(0,0,0,0.9);
  position: fixed;
  top: 0;
  left: 0;
  z-index: 10;
  animation: fadein 0.2s ease-out both;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  tap-highlight-color: transparent;
}
#pixel-viewer.fadeout {
  animation: fadeout 0.2s ease-out both;
  pointer-events: none /* ← IE救済措置(IEでこれがないと画像を閉じた後にページ全体がクリックできなくなります...)*/;
}
#pixel-viewer.fadeout #pixel-viewer__img {
  animation: scaledown 0.2s ease-out both;
}
#pixel-viewer::before,
#pixel-viewer::after {
  content: "";
  dispaly: block;
  width: 24px;
  height: 3px;
  background: #eee;
  border-radius: 4px;
  position: fixed;
  top: 20px;
  right: 12px;
  z-index: 12;
}
#pixel-viewer::before {
  transform: rotate(45deg);
}
#pixel-viewer::after {
  transform: rotate(-45deg);
}
#pixel-viewer__img {
  display: block;
  margin: auto;
  max-width: 100%;
  max-height: calc(100% - 44px);
  animation: scaleup 0.2s ease-out both;
}
@keyframes fadein {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}
@keyframes scaleup {
  0% {
    transform: scale(0.97);
  }
  100% {
    transform: scale(1);
  }
}
@keyframes fadeout {
  0% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}
@keyframes scaledown {
  0% {
    transform: scale(1);
  }
  100% {
    transform: scale(0.97);
  }
}

まとめ

素人設計ですが使いたいという方がいらっしゃいましたら、ご自由にお使いください。

このプラグインをJavaScriptを使わずCSSのみで再現するというお遊び記事も書いてるので、実用性はありませんが、よろしければご覧ください。

CSSのみでクリックで画像を拡大する(Lightboxもどき)