milvinae

web & product design.

変わったGalleryを力技で作る

,

横並びのGallery、グリッド表示のGallery…
……もう飽き飽きですわ!

そんなあなたにおすすめしたい、自分で張った写真のようなGalleryの作り方を紹介します。
まずは達成したい目標を明らかにしましょう。

<目標>
・任意の範囲内に画像をばらまく感じのギャラリーを作りたい
・ばらまくアニメーションがあると尚良し
・Javascriptで画像要素を生成して動的に処理したい


あんまり複雑な処理は必要なさそうですね。
結果から見てみましょう。

※Codepenで使用している画像はすべてPhotockさんのフリー素材です。
https://photock.jp/

See the Pen picture board by keiyashi (@keiyashi) on CodePen.


htmlはBackboardだけの超シンプル構成です。

画像たちはJavascriptで生成するのですが、具体的にはFigure要素(picture-frameクラス)の中にimg要素が入れる感じです。
次はそれらのcss(scss)を記しましょう。
.backboard{
  width: 90dvw;
  height: 90dvh;
  position:relative;
  left:5dvw;
  top:5dvh;
  background-image:  url("https://raw.githubusercontent.com/keiyashi/codepen-example/refs/heads/main/CorkBoard.jpg");
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
  padding: 15px;
  box-shadow: 2dvw 2dvw 2dvw rgba(64,64,64,.3);
  border-image-source: linear-gradient(-30deg, rgba(64,64,64,.9) 0%, rgba(128,64,32,.9) 60%);
  border-image-slice: 1;
  border-width: 13px;
  border: outset;
}

.picture-frame{
  position:absolute;
  width:15dvw;
  aspect-ratio:1.5;
  background-color:white;
  transform-origin:center;
  box-shadow:0px 0px 3px rgba(64, 64, 64, .8);
  img{
    position:relative;
    width:92%;
    height:92%;
    top:4%;
    left:4%;
    color:lightblue;
  }
  
}


縦長画像と横長画像を区別して表示したいという欲求はありました。
でもなんかうまくいかなくてムシャクシャしたので横長表示で統一してしまいました。
とにかく画像は横長Width15vwで縦横比はとにかく1.5だ!

cssに重要な部分は特にないですね。
グラデーションの枠や影など装飾的要素がいくつか入っています。



つぎはJavascriptです。

画像は扱いやすいように”pic-x.webp”という名前で統一、Githubにおいておきます。
こうすることでJSで画像パスを引っ張りやすくしました。
すでにフォーマットが違う画像たちを使ってギャラリーを作る場合は、名前を変えて再アップロードするかJS側でどうにか呼び出す方法を自分で考えるか諦めてプラグインを使ってください。

const picNumber = 17;
const picIdPrefix = "pic";
var   gitpass = "https://raw.githubusercontent.com/keiyashi/codepen-example/refs/heads/main/pic-";
var   fileSuffix = ".webp";

function initialize(){
  var back = document.querySelector(".backboard");
  
  for(i = 0; i < picNumber; i++){
    var picture = document.createElement("figure",{id: picIdPrefix + i}); 
    picture.classList.add("picture-frame");
    var pictureInner = document.createElement("img");
    pictureInner.src = gitpass + (i+1) + fileSuffix;
    picture.appendChild(pictureInner);
    back.appendChild(picture);
  }
}

function randomSet(){
  var back = document.querySelector(".backboard");
  var backRect = back.getBoundingClientRect();
  var innerAreaX = backRect.width * 1.2;
  var innerAreaY = backRect.height * 1.2;
  anime.set(".picture-frame", {
    // delay: 1000,
    translateX: function(){return anime.random(0, innerAreaX) + "px";},
    translateY: function(){return anime.random(0, innerAreaY) + "px";},
    rotate: function(){ return anime.random(-180, 180)},
  });
}

function randomMove(){
  var back = document.querySelector(".backboard");
  var backRect = back.getBoundingClientRect();
  var innerAreaX = backRect.width * 0.8;
  var innerAreaY = backRect.height * 0.8;
  anime({
    targets: ".picture-frame",
    delay: 500,
    duration: function(){ return anime.random(0, 1000)},
    easing: "easeOutSine",
    translateX: function(){return anime.random(0, innerAreaX) + "px";},
    translateY: function(){return anime.random(0, innerAreaY) + "px";},
    rotate: function(){ return anime.random(-180, 180)},
  });
}

document.addEventListener("DOMContentLoaded", (e) =>{
  initialize();
  randomSet();
  randomMove();
})

画像をばらまくアニメーションをつけたいのでanime.jsを使っています。
initializeで画像要素を生成してBack-boardに入れ込む ⇒ 画像たちの初期位置をSetする ⇒ 画像たちが初期位置からランダムに動く という流れになっています。


シンプルではありますが画像たちをバラバラに配置することが出来ました。
こうなってくると、一度に見せる画像を少なくして視認性を上げたり、選択した画像をアップで表示したりなど機能を追加したくなりますね。
後日、改良方法をアップしたいと思います。




<注意>
勘のいい方は画像が偏りがちなことに気付くでしょう。
これはanime.randomの中で使われているMath.randomが実装依存の擬似乱数であることが原因のようです。
※乱数生成の種となるシードが実装時点で決まってしまっているためユーザが変更することが出来ず、生成される乱数には偏りが出るみたいです。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Math/random

これは見た目上よろしくないので後日記事で解決方法を記載します。