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.