logo
コーディング

更新日

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

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

完成図

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

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: 0.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);
}

完成!

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