スマートフォン版のlightboxみたいなサンプル

ここのところ相変わらずスマートフォン対応ページの作業ばかりしてます。
最近、スマートフォン対応ページではjQuery(jQuery mobile)は読み込みに時間かかる!とかそんな流れっぽいので、その流れに乗ってjQueryは読み込まない方針でいくことにしました。
CSS3についてはOS・ブラウザ間で差はありますが、実際、JavaScriptに関してはOS・ブラウザでそんなに差がなく、それほど困ったことはないです。

で、今回は案件で実際に作った非jQueryなlightboxを簡単にしたサンプルを紹介。

とりあえずサンプルをご覧ください。

サンプルを見る
(PC:webkit搭載ブラウザ・iOS 5以上のSafariで確認。Androidは一部端末でちゃんと表示できるかも。)

「showBox」のボタンを押すと、lightboxが表示され、出てきたlightboxを押せば消えるだけのサンプルです。
基本、スマートフォンを想定してるので、タップできる範囲は「×」だけじゃなく、広めにlightbox全体にしてます。

簡単に解説。

lightbox部分を作ってみる

表示されるlightboxはdiv#boxというdiv要素なので、ひとまずコレをhtmlとCSSで作ってみます。

html

<div id="box">
  <img src="img/logo.png" alt="" />
</div>

CSS

#box {
    position: absolute;
    padding: 8px;
    background: #000;
    text-align: center;
    z-index: 100;
    -webkit-border-radius: 8px;
    -webkit-box-shadow: 0 0 8px rgba(0,0,0,0.3);
    -webkit-tap-highlight-color: rgba(0,0,0,0);
}

#box img {
    vertical-align: bottom;
}

#box:after {
    content: "×";
    position: absolute;
    right: -12px; top: -12px;
    width: 24px; height: 24px;
    background: #000;
    border: 2px solid #FFF;
    color: #FFF;
    font-size: 18px;
    font-weight: bold;
    line-height: 24px;
    text-align: center;
    -webkit-border-radius: 14px;
    -webkit-box-shadow: 0 0 8px rgba(0,0,0,0.3);
}

positionがabsoluteな割にleft・topが指定されてないのは、端末によって幅・高さが違うので、ボタンをクリックしたときに座標を求めるためです。
×ボタンは画像にしてもいいんですが、わざわざサンプル用に作るのがめんどくさかったのでafter擬似要素で作りました。

サンプルを見る

CSS3でanimationを作る

jQueryを使えば$(‘要素’).fadeIn()で簡単に作れますが、jQueryを使わないのでCSS3のanimationでフェードを再現します。

.animeShow {
    -webkit-animation-name: show;
    -webkit-animation-duration: 0.5s;
    -webkit-animation-timing-function: linear;
    -webkit-animation-fill-mode: forwards;
}

.animeHide {
    -webkit-animation-name: hide;
    -webkit-animation-duration: 0.5s;
    -webkit-animation-timing-function: linear;
    -webkit-animation-fill-mode: forwards;
}

@-webkit-keyframes show {
    0% {
        opacity: 0;
    }
    100% {
        opacity: 1;
    }
}

@-webkit-keyframes hide {
    0% {
        opacity: 1;
    }
    100% {
        opacity: 0;
    }
}

animationについて詳しい解説は省きます。
これで、div#boxのclassに「animeShow」を指定すればフェードイン・classに「animeHide」を指定すればフェードアウトするものができました。

サンプルを見る(フェードイン)

サンプルを見る(フェードアウト)

ちなみに、Androidでフェードアウトが終わってまた表示されてしまうのは、-webkit-animation-fill-mode: forwards;(animationの最終フレームで止める)がブラウザ側で実装されてないためです。
どうしも対応したい場合はJavaScript側で制御するなりCSSを改変したりしてみてください。

ひとまず、表示されるlightbox部分はこれで完成したので、あとはJavaScriptでdiv#boxを表示させるコードを書いてやるだけです。

lightboxを表示させるJavaScript

html

<p id="btn">
  <a class="btn btn-info btn-large">showBox</a>
</p>

htmlはボタンだけの記述になります。
ちなみにボタンはTwitter bootstrapのCSSを一部拝借。(まるごと拝借するとサイズが大きいので)

JavaScript

var btn = document.getElementById('btn').children[0];
btn.addEventListener('click',showBox,false); //btnのclickイベント登録

p#btnの0番目の子要素(aタグ)にclickイベントを登録して、showBoxを呼び出すようにします。

function showBox(e){
  var btn = e.target;
  btn.removeEventListener('click',showBox,false); //btnのclickイベント削除
  btn.className = 'btn btn-warning btn-large'; //btnのクラス名変更
}

まだ登場してませんが、showBox関数内でdiv#boxを作ることになります。
このままではボタンをクリックした数だけdiv#boxが生成されてしまうので、ボタンのclickイベントを削除してクリックしてもshowBox関数が呼び出されないようにします。
ボタンの色が変わるのはおまけ。

  //省略
  btn.className = 'btn btn-warning btn-large'; //btnのクラス名変更

  //画像
  var img = new Image();
  img.src = 'img/logo.png';

  //画像を読み込んでから表示させる
  img.onload = function(){

  }

じゃあ、早速div#boxを作っていきたいところですが、まず先に画像の読み込みを行います。
画像が読み込み終わってないのに先々やってしまうと、うまくwidthやheightの値を取得することができないためです。

  //省略
  img.onload = function(){
    //div#boxの作成
    var box = document.createElement('div');
    box.id = 'box';
    box.style.opacity = '0';

    box.appendChild(img); //div#boxに画像を追加
    document.body.appendChild(box); //bodyにdiv#boxを追加
  }

img.onload内でようやくdiv#boxを作成します。
div#boxを作成したら、読み込んだimgをdiv#box内に追加して、body内にdiv#boxを追加します。

できることなら、body内に追加する前にleft・topを計算したいんですが、boxの幅・高さの値を調べるとまだ0なので、後で計算するとして、div#boxにopacityの値を0にして見えなくしておきます。

/* 省略 */
<div id="box" style="opacity: 0;">
  <img src="img/logo.png" alt="" />
</div>

この時点でhtmlはこんな感じになってる。

  //省略
  img.onload = function(){
    //省略

    //中央に寄せるための座標計算
    var left = (window.innerWidth - box.offsetWidth) / 2;
    var top = (window.innerHeight - box.offsetHeight) / 2;

    //styles
    box.style.left = left + 'px';
    box.style.top = top + 'px';
    box.style.cursor = 'pointer';
  }

body内に追加した後だと、box.offsetWidth・box.offsetHeightの値がちゃんと取得できるので、ここで計算します。
計算したleftとtopをstyleに追加します。pxを忘れないように。
opacity: 0;なため、視覚的には見えませんが、これでブラウザ内の縦横中央にlightboxが配置されている状態にあります。

cursorをpointerにしたのはPC用なので、スマートフォンだけなら別に必要なし。

/* 省略 */
<div id="box" style="opacity: 0; left: 537px; top: 361px; cursor: pointer;">
  <img src="img/logo.png" alt="" />
</div>

この時点でのhtml。(ウィンドウサイズ:1280*1024の場合。環境によって値は変わります。)

  //省略
  img.onload = function(){
  //省略

    //animation
    box.className = 'animeShow';
  }

lightboxが縦横中央に配置されたところで、div#boxにclass「animeShow」を追加してあげれば、アニメーションが開始されます。

  //省略
  img.onload = function(){
  //省略

    //boxのclickイベント登録
    box.addEventListener('click',function(){
      //animation
      this.className = 'animeHide';
      //clickイベント削除
      this.removeEventListener('click',arguments.callee,false);

      //アニメーションが終わったら
      this.addEventListener('webkitAnimationEnd',function(){
        document.body.removeChild(box);
        box = null;
      },false);

      //ボタンのクラス名変更・clickイベント登録
      btn.className = 'btn btn-info btn-large';
      btn.addEventListener('click',showBox,false);
    },false);
  }

あとはboxにclickイベントを登録して、押した時に消してやるだけです。
アニメーションが終わった時に自身をbodyから消してやらないと、どんどんdiv#boxが溜まっていくので消し忘れないように注意です。

まだまだ横向きにした時の対応だったり、Android対応だったりと改善する点はありますが、このコード量であれば外部ファイルにすればたった1.5KB程度です。

みなさんも脱jQuery始めてみませんか?

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


この記事をブックマーク・シェアする

この記事に関連する記事

カテゴリ

コメント

コメントを残す

コメント*

コメントリンクを nofollow free に設定することも出来ます。

誤字・脱字やリンク切れなどお気づきの点がございましたら@loop0429までよろしくお願いします。

この記事の先頭へ