Vue.jsでカテゴリー分けされた画像リストを実装

たくさんある商品画像やポートフォリオの作品一覧などを見せたいときに、カテゴリーごとに絞って表示できたら便利ですよね。Vue.jsを使えば同じページのまま表示画像を変更できるので、管理も楽になりますよ。

↑私が10年以上利用している会計ソフト!

完成図

まずは画像を一覧で表示させ、上部にあるカテゴリー名のボタンをクリックすると、そのカテゴリーに属する画像だけを表示させます。

1. Vue.jsの読み込み設定

まずはHTMLの head 内にVue.jsを読み込ませます。別のバージョンやNPMでのインストール方法は公式サイトを参照してください。

<script src="https://unpkg.com/vue@3.0.11/dist/vue.global.js"></script>

2. 画像を一覧表示

Vue.jsの配列を使って画像を用意します。各画像に index として番号を割り振っていますがここは画像のタイトルや商品名などでもOK。個別に識別するために設定します。src には画像のファイルパス、category に設定したりカテゴリー名を記述。

JavaScript

const Gallery = {
  data() {
    return {
      images: [
        { index: 1,
          src: "images/1.jpg",
          category: "Dog"
        },
        { index: 2,
          src: "images/2.jpg",
          category: "Cat"
        },
        { index: 3,
          src: "images/3.jpg",
          category: "Dog"
        },
        { index: 4,
          src: "images/4.jpg",
          category: "Cat"
        },
        { index: 5,
          src: "images/5.jpg",
          category: "Cat"
        },
        { index: 6,
          src: "images/6.jpg",
          category: "Dog"
        },
      ]
    };
  },
};
Vue.createApp(Gallery).mount("#app");

HTMLではリストに v-for を使って用意した画像を一覧表示させます。key 属性を全てのアイテムに与える必要があるので、上記で設定しておいた index を値として設定。この key がないとエラーになり、うまく表示されなかったりします。

HTML

<div id="app">
  <ul class="category-list">
    <li v-for="image in images" :key="image.index">
      <img :src="image.src" alt="">
    </li>
  </ul>
</div>

CSSで装飾

画像はCSSグリッドを使ってタイル型レイアウトにしました。auto-fit を使ってレスポンシブにも対応。このレイアウトの設定方法は、過去記事「CSS Gridを使ったレスポンシブ対応の基本レイアウト」を参照してください。

.category-list {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.category-list img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}


ここまででこんな感じに表示されました!

3. カテゴリーボタンを作成

画像リストの上にカテゴリーボタンを設置します。今回はラジオボタンを使ってどのカテゴリーを選択したのかを判別します。

HTML

<div id="app">
<!-- ↓ 追加 ↓ -->
  <div class="category-nav">
    <label>
      <input type="radio" v-model="category" value=""> All
    </label>
    <label>
      <input type="radio" v-model="category" value="Dog"> Dog
    </label>
    <label>
      <input type="radio" v-model="category" value="Cat"> Cat
    </label>
  </div>
<!-- ↑ 追加 ↑ -->
  
  <ul class="category-list">
    <li v-for="image in images" :key="image.index">
        <img :src="image.src" alt="">
    </li>
  </ul>
</div>


ラジオボタンが加わりました。

CSSで装飾

このままだと不格好なので、ラジオボタンをCSSで装飾していきます。ラジオボタン自体は非表示にしていますが、クリックされたかなどの判定機能は残っています。詳しくは「CSSとSVGでチェックボックスを装飾しよう!」も読んでみてください。チェックボックスとラジオボタンの装飾方法は基本的には変わりません。

CSS

.category-nav {
  text-align: center;
  padding: 30px;
}
.category-nav label {
  background: #0bd;
  color: #fff;
  padding: 5px 10px;
  margin: 10px;
  cursor: pointer;
}
.category-nav input[type="radio"] {
  /* Hide radio buttonns */
  opacity:0;
  -webkit-appearance: none;
  appearance: none;
  position: absolute;
}


ボタンらしく装飾しました。

4. カテゴリーごとにフィルターを加える

すべて画像が表示されただけで、まだカテゴリー別に表示はされないので、新たに関数を使って表示させる画像を振り分けます。data(){} の下に computed を追加し、JavaScriptの filter() 関数を使いましょう。ユーザーが選択したカテゴリーと一致するカテゴリーがあるか判定し、一致したもののみを結果として反映してくれます。

また、data 内には category: '', を追加して、初期カテゴリーを空に(=すべての画像を表示)します。

JavaScript

const Gallery = {
  data() {
    return {
      category: '', // ←------- 追加
      images: [
        // 〜〜 省略 〜〜
      ]
    };
  },

// ↓ 追加 ↓
  computed: {
    filterByCategory: function () {
      return this.images.filter(
        (image) => !image.category.indexOf(this.category)
      );
    }
  }
// ↑ 追加 ↑
};
Vue.createApp(Gallery).mount("#app");

HTMLでは v-for で繰り返し表示させていたループの部分を少し変更します。image in images としていたところを image in filterByCategory にしています。上記で設定したフィルター関数を通して画像を表示させる指示ですね。

HTML

<li v-for="image in filterByCategory" :key="image.index">
  <img :src="image.src" alt="">
</li>

上部のボタンをクリックしてカテゴリー別に画像が表示されています!

5. アニメーション

このままでも機能としては十分ですが、さらにふんわり表示させるアニメーションを加えるといいですね!Vue.jsでは transitionコンポーネントが用意されているので、任意のCSSアニメーションを加えるだけでいい感じに動いてくれますよ。

HTMLでは画像を ul で囲んでいた部分を transition-group に置き換えます。リストを最初に読み込んだ時にもふんわり動かしたいので appear 属性を追加。tag 属性の値を ul にすることで、ul として描画されます。

HTML

<!-- ↓ ul だったところを書き換え -->
<transition-group appear tag="ul" class="category-list">
  <li v-for="image in filterByCategory" :key="image.index">
    <img :src="image.src" alt="">
  </li>
</transition-group> <!-- ← ul だったところを書き換え -->

transition コンポーネントでは最初からアニメーションのタイミングごとにクラス名がついています。各クラス名やタイミングなどは公式サイトを読んでみましょう。

CSSでは transition プロパティーで透明度や画像の位置を変更しています。

CSS

.v-enter-active,
.v-enter-active,
.v-leave-active,
.v-move {
  transition-property: transform, opacity;
  transition-duration: .3s;
}
.v-enter-active {
  opacity: 0;
  transform: translateY(50px);
}
.v-enter-to,
.v-leave-active {
  opacity: 1;
  transform: translateY(0);
}
.v-leave-to {
  opacity: 0;
  transform: translateY(50px);
}

完成!

アニメーションを工夫すればもっといい感じにできそうですね。好みにあわせてあれこれカスタマイズしてみてください!

See the Pen
Galley Filter using Vue.js
by Mana (@manabox)
on CodePen.

シェアする

ニュースレター

Web制作の最新情報やWebクリエイターボックスからのお知らせ、中の人の近況等を定期的にお送りいたします。 ぜひご登録ください!もちろん無料です! :)