枚数が増えても大丈夫。画面いっぱいに広がる写真ギャラリーをサクっと作りました

この記事を書いたひと: @t4traw 2019年1月17日

「イベントで流す写真ギャラリーがあるのだけど、毎回写真を取り込んでムービーを作るのが大変!多い時は2-3日かかっちゃう。なんとか楽にできる方法ないですか?」というご相談をいただきました。

今まで作ったムービーを見させていただいたところ、写真の件数が多いのでタイル上に並べて、写真にタイトルが付いている形でした。ふむふむ、これならサクっとhtml/css/jsで表示するものが作れそう。

こんな感じの提案をしました

というわけで、以下の提案をしました。

  • フォルダーを選択して、ブラウザで表示するタイプのアプリケーション
    • 枚数が増えてもフォルダーを選択するだけで生成されます
    • ファイル名をタイトルにすることで表示も管理も簡単に
    • ブラウザだけで完了するので環境を選びません

制作開始

というわけで、まずはhtml。簡単にjsで描画するための要素とフォルダーの選択ボタンを設置します。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Simple photo gallery</title>
<link rel="stylesheet" href="animate.min.css"></link>
<link rel="stylesheet" href="style.css"></link>
</head>
<body>
  <div id="app">
    <input class="" type="file" webkitdirectory directory onChange="generateGallery(this.files)">
  </div>
  <script src="app.js"></script>
</body>
</html>

とくに難しいことはなく、#app要素に選択したフォルダーの中身を書き込んでいきます。

次に肝心のjsです。

function sleep(milsec) {
  return new Promise(resolve => setTimeout(resolve, milsec));
}

async function generateGallery(files) {
  let file_counter = files.length
  let file_name = new Array()
  let file_path = new Array()
  let images_counter = 0

  document.getElementById('app').textContent = null

  for (let i = 0; i < file_counter; i++) {
    if (!files[i].name.match(/\.jpg|\.jpeg|\.png/)) { continue };
    file_name.push(files[i].name.match(/(.*)(?:\.([^.]+$))/)[1])
    file_path.push(files[i].webkitRelativePath)
  }

  for (let i = 0; i < 6; i++) {
    let image = document.createElement('div')
    image.classList.add('slides')

    image.style.backgroundImage = 'url(' + file_path[images_counter] + ')'
    image.classList.add('animated')
    image.classList.add('slow')
    image.classList.add('fadeIn')

    let desc = document.createElement('p')
    let text = document.createTextNode(file_name[i])
    desc.classList.add("description")
    desc.appendChild(text)

    image.appendChild(desc)
    document.getElementById('app').appendChild(image)
    images_counter++
  }

  slides = document.getElementsByClassName('slides')
  while (true) {
    for (let i = 0; i < 6; i++) {
      await sleep(5000)

      let newNode = document.createElement('div')
      newNode.style.backgroundImage = 'url(' + file_path[images_counter] + ')'
      newNode.classList.add('slides')
      newNode.classList.add('animated')
      newNode.classList.add('slow')
      newNode.classList.add('fadeIn')
      newNode.style.backgroundImage = 'url(' + file_path[images_counter] + ')'

      let desc = document.createElement('p')
      let text = document.createTextNode(file_name[images_counter])
      desc.classList.add("description")
      desc.appendChild(text)
      newNode.appendChild(desc)

      document.getElementById('app').replaceChild(newNode, slides[i])
      images_counter++
      if (images_counter >= (file_counter - 1)) {
        images_counter = 0
      }
    }
  }
}

順番に変更していきたいのでsleep関数を作成し、取得したファイルの一覧を#appに書き込みます。

あとは画面いっぱいに表示されるようにスタイルを作成します。

* {
  margin: 0;
  padding: 0;
  border: 0;
}

body {
  width: 100vw;
  height: 100vh;
}

body #app {
  display: flex;
  flex-wrap: wrap;
}

body #app .slides {
  background-size: cover;
  background-position: center center;
  width: 33%;
  height: 50vh;
  position: relative;
}

body #app .slides .description {
  background-color: rgba(0, 0, 0, 0.5);
  color: #fff;
  text-align: center;
  padding: 10px;
  position: absolute;
  bottom: 0;
  right: 0;
}

100vhやflexなどをうまく使ってレスポンシブにすることで画面のアスペクト比やサイズを気にせず使えます。

フェイドインもcssで書こうと思いましたが、animatecssのほうが簡単かつキレイに表現できそうだったのでそちらを使うことにしました。

ちょっとgifアニだとフェイドインがうまくキャプチャできないのですが、いい感じに動いています。

あとはF11などの全画面化をすることでディスプレイいっぱいに表示することが可能です。


こんな感じでサクっと特別なソフトやコードを書かずに実装できました。

css3になって表現したいことが簡単にできるようになって本当に良い時代になりました。

今回のコードをGitHubにpushしておきました。もしよかったら自分に改造して使ってください。

t4traw/simple_photo_gallery

それでは。