フロントエンド

【Vue.js】かっこいいフォトギャラリーを発見したのでソースを読んでみた。カテゴリーで選べてなめらかに動くんです

アオキ
写真をかっこよく見せたいなぁ、できたらVue.jsで。

と思いググっていたら、見つけました。

See the Pen
VueJS Category Filter CSS Animation
by aoki_monpro (@suica)
on CodePen.

実際に動くページはこちら

アオキ
うーん、かっこいい。

写真ごとにカテゴリーが設定されていて、

ARTやWORKSHOPSと書かれている箇所をクリックすると、カテゴリーごとに表示されて、

ALLを押すと全ての写真が表示されると。

動きもスムーズ。いい感じ。

アオキ
これはぜひ自分でもつくれるようになりたいっ・・!

という事でソースを読んでみることにしました。

Sponsored link

かっこいいフォトギャラリーのソースコードを読んでみる

ソースコードはこんな感じです。

<!DOCTYPE html>
<meta charset="utf-8">
<head>
    <style>
    html,body {
    margin:0;
    font-family: 'Dawning of a New Day', cursive;
}

.title-container {
    display:flex;
    flex-direction:column;
    justify-content:center;
    align-items:center;
}

.title {
    font-family: 'Dawning of a New Day', cursive;
    font-size:30pt;
    font-weight:normal;
}

.project-title {
font-size:16pt  
}

.filter {
    font-family:arial;
    padding: 6px 6px;
    cursor:pointer;
    border-radius: 6px;
    transition: all 0.35s;
}

.filter.active {
    box-shadow:0px 1px 3px 0px #00000026;
}

.filter:hover {
    background:lightgray;
} 

.projects {
    margin-top:25px;
    display:flex;
    flex-wrap:wrap;
    justify-content:center;
}

.projects-enter {
    transform: scale(0.5) translatey(-80px);
    opacity:0;
}

.projects-leave-to{
    transform: translatey(30px);
    opacity:0;
}

.projects-leave-active {
    position: absolute;
    z-index:-1;
}

.circle {
    text-align:center;
    position:absolute;
    bottom:-38px;
    left:40px;
    width:100px;
    height:100px;
    border-radius:50px;
/*  border:1px solid black; */
    display:flex;
    box-shadow: 0px -4px 3px 0px #494d3257;
    justify-content:center;
    align-items:center;
    background-color:#fff;
/*  box-shadow:0px -3px 3px #484848a6; */
}

.project {
    transition: all .35s ease-in-out;
    margin:10px;
    box-shadow:0px 2px 8px lightgrey;
    border-radius:3px;
    width:180px;
    height:200px;
    display:flex;
    flex-direction:column;
    align-items:center;
}

.project-image-wrapper {
    position:relative;
}

.gradient-overlay {
    position:absolute;
    top:0;
    left:0;
    width:100%;
    height:150px;
    opacity:0.09;
    background: 
        linear-gradient(to bottom, rgba(0,210,247,0.65) 0%,rgba(0,210,247,0.64) 1%,rgba(0,0,0,0) 100%), 
        linear-gradient(to top, rgba(247,0,156,0.65) 0%,rgba(247,0,156,0.64) 1%,rgba(0,0,0,0) 100%);
    border-bottom-left-radius:10px;
    border-bottom-right-radius:10px;
    border-top-left-radius:3px;
    border-top-right-radius:3px;
}

.project-image {
    width:100%;
    height:150px;
    border-bottom-left-radius:5px;
    border-bottom-right-radius:5px;
    border-top-left-radius:3px;
    border-top-right-radius:3px;
}    
    
    </style>
</head>
<body>
<div id="app">
    <div class="title-container">
        <div>
            <h3 class="title">
                Our Projects
            </h3>
        </div>
        <div class="filters">
            <span class="filter" v-bind:class="{ active: currentFilter === 'ALL' }" v-on:click="setFilter('ALL')">ALL</span>
            <span class="filter" v-bind:class="{ active: currentFilter === 'ALL' }" v-on:click="setFilter('ART')">ART</span>
            <span class="filter" v-bind:class="{ active: currentFilter === 'WORKSHOPS' }" v-on:click="setFilter('WORKSHOPS')">WORKSHOPS</span>
            <span class="filter" v-bind:class="{ active: currentFilter === 'FUN' }" v-on:click="setFilter('DOODLES')">DOODLES</span>
        </div>
    </div>

    <transition-group class="projects" name="projects" >
        <div class="project" v-if="currentFilter === project.category || currentFilter === 'ALL'" v-bind:key="project.title" v-for="project in projects">
            <div class="project-image-wrapper">
                <img class="project-image" v-bind:src="project.image">
                <div class="gradient-overlay"></div>
                <div class="circle">
                    <span class="project-title">{{project.title}}</span>
                </div>
            </div>
        </div>
    </transition-group>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.22/dist/vue.js"></script>

<script>
new Vue({
    el: '#app',
    data: {
        currentFilter: 'ALL',
        projects: [
            {title: "Artwork", image: "https://picsum.photos/g/200?image=122", category: 'ART'},
            {title: "Charcoal", image: "https://picsum.photos/g/200?image=116", category: 'ART'},
            {title: "Sketching", image: "https://picsum.photos/g/200?image=121", category: 'DOODLES'},
            {title: "Acrillic", image: "https://picsum.photos/g/200?image=133", category: 'WORKSHOPS'},
            {title: "Pencil", image: "https://picsum.photos/g/200?image=134", category: 'DOODLES'},
            {title: "Pen", image: "https://picsum.photos/g/200?image=115", category: 'ART'},
            {title: "Inking", image: "https://picsum.photos/g/200", category: 'WORKSHOPS'},
        ]
    },
    methods: {
        setFilter: function(filter) {
            this.currentFilter = filter;
        }
    }
})

</script>

</body>
</html>

大きく分けると、

  • ホームページを表示する『HTML』と、
  • 見た目をいい感じにする『CSS』と、
  • 最近人気急上昇中の『Vue.js(JavaScriptのフレームワーク)』

の3点セットでなりたっています。

説明の都合上、まずはscriptタグの中から見てみます。

かっこいいフォトギャラリー 『Vue.js』の読み込みなど

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.22/dist/vue.js"></script>

<script>
new Vue({
    el: '#app',
    data: {
        currentFilter: 'ALL',
        projects: [
            {title: "Artwork", image: "https://picsum.photos/g/200?image=122", category: 'ART'},
            {title: "Charcoal", image: "https://picsum.photos/g/200?image=116", category: 'ART'},
            {title: "Sketching", image: "https://picsum.photos/g/200?image=121", category: 'DOODLES'},
            {title: "Acrillic", image: "https://picsum.photos/g/200?image=133", category: 'WORKSHOPS'},
            {title: "Pencil", image: "https://picsum.photos/g/200?image=134", category: 'DOODLES'},
            {title: "Pen", image: "https://picsum.photos/g/200?image=115", category: 'ART'},
            {title: "Inking", image: "https://picsum.photos/g/200", category: 'WORKSHOPS'},
        ]
    },
    methods: {
        setFilter: function(filter) {
            this.currentFilter = filter;
        }
    }
})

</script>

まず最初に、

<script src="https://cdn.jsdelivr.net/npm/vue@2.5.22/dist/vue.js"></script>

という箇所で、『Vue.js』のファイルを読み込んでいます。

『Vue.js』を使うには大きく2つ方法がありますが、

  1. 『Vue.js』をインストールする方法
  2. インターネット上のファイルを読み込む方法

今回はインターネット上のファイルを読み込んでいます。

インターネット上のファイルを読み込む方法のことを、『CDN』と読んだりします。

『CDN』・・コンテンツデリバリーネットワーク

インターネット上から読み込んだ後、下記のコードが書かれています。

new Vue({
    el: '#app',
    data: {
        currentFilter: 'ALL',
        projects: [] // 一旦カット
    },
    methods: {
        setFilter: function(filter) {
            this.currentFilter = filter;
        }
    }
})

new Vueとして、『Vue.js』を有効にして(インスタンス化)して、

el: の後に、『Vue.js』の適用範囲を決めます。

なんでもいいのですが慣例で #app とすることが多いです。

『HTML』側、bodyのすぐ下から #app で囲むことで、body全体を『Vue.js』が効くようにします。

Sponsored link

かっこいいフォトギャラリー data編

次はdata。

文字通りデータを入れておく箱のことで、今回はdataの中に、

  • currentFilter
  • projects

の2つのデータの箱(変数)を用意しています。

初期値を入力できるので、

  • currentFilter: ‘ALL’,

で、最初はALLが選択された状態にしています。

  • projects

の中はオブジェクトの配列になっていて、

キー:値

の組み合わせでいくつかの情報が書かれています。

{title: "Artwork", image: "https://picsum.photos/g/200?image=122", category: 'ART'},

の場合だと、

  • title が Artwork
  • image が https://picsum.photos/g/200?image=122 (画像ファイル)
  • category が ART

といった具合です。

今回は合計7つ、カテゴリーとしては、ARTとDOODLESとWORKSHOPSの3種類が書かれています。

アオキ

配列なのでもちろんもっと増やすことも可能です。
projects: [
            {title: "Artwork", image: "https://picsum.photos/g/200?image=122", category: 'ART'},
            {title: "Charcoal", image: "https://picsum.photos/g/200?image=116", category: 'ART'},
            {title: "Sketching", image: "https://picsum.photos/g/200?image=121", category: 'DOODLES'},
            {title: "Acrillic", image: "https://picsum.photos/g/200?image=133", category: 'WORKSHOPS'},
            {title: "Pencil", image: "https://picsum.photos/g/200?image=134", category: 'DOODLES'},
            {title: "Pen", image: "https://picsum.photos/g/200?image=115", category: 'ART'},
            {title: "Inking", image: "https://picsum.photos/g/200", category: 'WORKSHOPS'},
        ]

かっこいいフォトギャラリー methods編

次はmethods(メソッド)。

    methods: {
        setFilter: function(filter) {
            this.currentFilter = filter;
        }
    }

メソッドの名前はなんでもいいのですが、

読んで意味がわかるように『名詞』『動詞+名詞』がいいとされています。

今回はフィルターをセットするということで 『setFilter』。

そのままですね。

メソッドの中身にまるっと関数をいれています。

アオキ
名前なしの関数です、クロージャと言ったりもします。

引数に filter として、カテゴリーの値をとるようにして、

this.currentFilter というのは 先ほどのdata のcurrentFilter になります。

例えばカテゴリーがARTだったら、

dataのcurrentFilterも ART に変わる、という具合ですね。

Sponsored link

かっこいいフォトギャラリー 『HTML』内に独特の書き方が満載

『Vue.js』の大きな特徴として、

『HTML』の中に、『Vue.js』独特の書き方を織り交ぜる事ができます。

『ディレクティブ』と呼ばれます。

最初はとっつきにくいかもですが、

『HTML』内のどの場所が変化するかというのがわかりやすいので、

慣れるとだいぶ読みやすいつくりになっています。

今回のフォトギャラリーの場合、合計6つの『Vue.js』独特の書き方(『ディレクティブ』)がありました。

  • v-on:click ・・クリックした時に動作する(メソッドを指定する)
  • v-bind:class ・・クラスを紐づける
  • transition-group ・・いい感じに動かす
  • v-if ・・ カテゴリーによって表示を振り分ける
  • v-for ・・繰り返す
  • v-bind:key ・・ v-forの時は必ずkeyをつける
  • v-bind:src ・・ 画像のURL
アオキ
ほとんど 『v-』から始まっているのでわかりやすいかも?ですね。

上から順に解説してみます。

クリックしたら処理を実行する v-on:click

カテゴリーの箇所のコードは下記になっています。

        <div class="filters">
            <span class="filter" v-bind:class="{ active: currentFilter === 'ALL' }" v-on:click="setFilter('ALL')">ALL</span>
            <span class="filter" v-bind:class="{ active: currentFilter === 'ART' }" v-on:click="setFilter('ART')">ART</span>
            <span class="filter" v-bind:class="{ active: currentFilter === 'WORKSHOPS' }" v-on:click="setFilter('WORKSHOPS')">WORKSHOPS</span>
            <span class="filter" v-bind:class="{ active: currentFilter === 'FUN' }" v-on:click="setFilter('DOODLES')">DOODLES</span>
        </div>
v-on:click="setFilter('ALL')" 

という箇所で、クリックした時にsetFilterメソッド(引数はALL)が実行されます。

(data.currentFilter の中身がALLになります。)

v-on:click="setFilter('ART')"

をクリックしたら、 setFilterメソッド(引数はART)が実行ですね。

ちなみに、v-on:clickは @click と短く書くこともできます。

条件指定でクラスをつける v-bind:class

v-bindはちょっととっつきにくいかもですが、bindは英語で結びつけるという意味で、『CSS』のクラスとdataの値を結びつける事ができます。

v-bind:class="{ active: currentFilter === 'ALL' }" 

の書き方だと、
currentFilter が ALL なら class を active にする、という意味になります。

『CSS』側で、 .active のスタイルを書いています。

 .filter.active {
    box-shadow:0px 1px 3px 0px #00000026;
}

box-shadowなのでうっすら影をつけるイメージでしょうか。

よくよくみると、確かにうっすら影が見えます。

アオキ
この辺はこだわりというかセンスというか・・ですね。
Sponsored link

いい感じに動かす transition-group

カテゴリーボタンを押した時に、

にゅ〜っと動いたり、

ふわっと消えたり、

なんというかこういい感じに動いているんですが、(語彙力・・)

それを実現する方法が、『transition-group』というディレクティブになります。

『transition-group』で囲った箇所がいい感じに動く対象の範囲になります。

先ほど v-bind:class で active というクラスをつけていましたが、

『transition-group』も同じように、動作によってクラスをつけたり消したりします。

『Vue.js』公式の画像を拝借すると、

  • v-enter・・動作開始のタイミング(フェードインのタイミング)
  • v-enter-active ・・動作中
  • v-enter-to ・・動作が終わったタイミング
  • v-leave・・消え始めるタイミング(フェードアウトのタイミング)
  • v-leave-active ・・消えている間
  • v-leave-to ・・消えたタイミング

の6つのクラスが瞬時についたり消えたりします。

今回の場合、transition-group タグで囲った中に、画像表示のコードが書かれているのですが(v-ifやv-forなど)、

これら全てに クラスがついたり消えたりします。

    <transition-group class="projects" name="projects" >
        <div class="project" v-if="currentFilter === project.category || currentFilter === 'ALL'" v-bind:key="project.title" v-for="project in projects">

実際にクラスがついたり消えたりする動画がこちら。

一瞬なので、

アオキ
い、いったい何が起こってるんや・・

な状態ですが、よくみると確かにクラスがついたり消えたりしているのがわかるんじゃないかなと思います。

(グーグルクロムで右クリック->検証 を押すと確認できます。)

『CSS』側を見ると、 projects というクラス以外に、

  • projects-enter
  • projects-leave-to
  • projects-leave-active

というクラスも用意されていることがわかります。

.projects {
    margin-top:25px;
    display:flex;
    flex-wrap:wrap;
    justify-content:center;
}

.projects-enter {
    transform: scale(0.5) translatey(-80px);
    opacity:0;
}

.projects-leave-to{
    transform: translatey(30px);
    opacity:0;
}

.projects-leave-active {
    position: absolute;
    z-index:-1;
}

これらのクラスが一瞬でついたり消えたりします。

アオキ
いやーすごい。

『CSS』周りの解説は長くなるので別記事にて。

条件付きで繰り返す v-if v-for v-bind:key

※追記 v-for とv-ifの組み合わせは非推奨です。(v-forが優先される)

transition-group で囲った中はこんなコードになっています。

<div class="project" v-if="currentFilter === project.category || currentFilter === 'ALL'" v-bind:key="project.title" v-for="project in projects">
〜省略〜
</div>

v-forは繰り返す構文で、下記のように書きます。

v-for="project in projects"

v-for = 単数形 in 複数形

単数の箇所は実際はなんでもよくて、例えば

v-for = "test in projects"

でも動くんですが、見た目がわかりづらいので、

v-for = 単数形 in 複数形

で書くのが慣例になっています。

また、

v-forで繰り返す場合は、v-bind:keyが必ず必要になります。(それぞれを識別できるようにするため)。

今回は dataの、projectの中の、title をキーにしています。

v-bind:key="project.title"

ですね。

『v-if』は文字通り条件指定で、

v-if="currentFilter === project.category

もし、 currentFilter が、 project.categoryだったら・・ (project.categoryというのはdataの中の、projectの中のcategory)、

  • ARTならARTを表示して、
  • DOODLEならDOODLEを、
  • WORKSHOPS なら WORKSHOPSを表示、という意味になります。
 v-if="currentFilter === project.category || currentFilter === 'ALL'"

OR演算子(||) がついていて、 ALL だったら、という条件もあるので、

アオキ
どれでも条件満たすなら v-if つけなくてもいいんじゃない・・?

と思い v-ifを消してみたら見事に動かなくなったので、動くきっかけとしてv-ifは必要のようです。

最後に、 v-bind:src=”project.image” は見たまま、画像のアドレスを projectからもってきてます。

Sponsored link

かっこいいフォトギャラリー のソースコードを読んでみて

なによりびっくりしたのは、

『transition-group』で囲うだけで、6つのクラスが瞬時についたり消えたりするとこですね。

つくクラス名も法則が決まっているので、

慣れるとかなりかっこいいフォトギャラリーがさくっとつくれるようになりそうな。

今回は解説していませんが、『CSS』の『Flexbox』という技術も使っているのでスマホでもばっちり表示されますし。

実際に動くページはこちら

アオキ
ぜひマスターしてかっこいいフォトギャラリーをさくっとつくれるようになりたいですね〜

参考ページ Using Vuejs transitions to filter the projects by category

created by Rinker
¥3,699 (2024/03/28 18:05:45時点 Amazon調べ-詳細)

『Vue/Vuex/Nuxt』ではこんな記事も読まれています。

1. 【Vue.js】初心者向けの動画をリリースしました【Udemy】

2. 『Vue.js』サンプルアプリの作り方動画を追加しました【Udemy】

3. Vue.js3のセクションを追加しました【Udemy】【Vue.js】

4. 【JavaScript】初心者向けの動画をリリースしました【Udemy】

5. 【Vue.js】と【Firebase】で作るミニWebサービス を試してみた〜Googleログインまで〜

6. 【Nuxt】入門 Vuexの状態管理を【図解】してみた【初心者向け】

7. 【Laravel5.5】Webアプリケーションを作るためのゆるめの環境構築編【Node.js】【npm】【Vue.js】【初心者向け】

8. 【Laravel】と【Vue.js】のサンプル動画を見ながらさらりと解説してみる

9. 【Vue.js】【SPA】の作り方をわかりやすくまとめてみた【初心者向け】

10. 【Vue.js】かっこいいフォトギャラリーを発見したのでソースを読んでみた。カテゴリーで選べてなめらかに動くんです

アオキ
ツイッターでも記事ネタ含めちょろちょろ書いていくので、よろしければぜひフォローお願いしますm(_ _ )m

アオキのツイッターアカウント


関連記事一覧 (一部広告あり)

コメント

  1. ためになる記事ありがとうございます。
    自分はVueCLI-3で実装しようとしたのですが、その場合v-if,v-forを同時に使うとエラーが起きるようです。
    この問題はv-ifの部分を削除して、computedを追記することで解決できました。

    computed: {
    activeProjects: function() {
    return this.projects.filter(project => {
    return this.currentFilter === project.category || this.currentFilter === ‘ALL’
    })
    }
    }

      • aoki_monpro
      • 2020年 10月 12日 2:46pm

      コメントありがとうございます。
      v-if と v-for の同時使用はよくないですね、
      computedでのコードありがとうございます、参考にさせていただきます。

    • AAA
    • 2019年 2月 10日 2:00pm

    丁度Vueでのこのようなフィルター機能を探していました!

    そこで質問なのですが、1つの作品に複数のカテゴリをつけたい場合(例えば、Artworkに”ART”と”WORKSHOP”)、どのようにcategoryを設定すれば良いのでしょうか?

    また、それぞれに別ページのリンクをつけたいのですが(“/Artwork”の様に)、どの様にできますでしょうか?
    Nuxtでやっているので、などとしてみましたが、上手くいきませんでした。。。

    もしご回答いただけますと幸いです!

      • aoki_monpro
      • 2019年 2月 10日 10:37pm

      複数カテゴリ 配列かませばいけるだろうとしばらくググってみたのですが、確かにうまく動かないですね、わかり次第追記したいと思います。

      リンクは category/:id などのようにカテゴリごとにページ作ってその下に番号ふる形でいいのではと思います。
      vue-routerの動的ルートマッチングでいけそうな。
      https://router.vuejs.org/ja/guide/essentials/dynamic-matching.html

        • Masa
        • 2019年 11月 14日 11:40am

        v-if文とv-for文は推奨されないのでそのせいかもしれません。
        とりあえずこんな感じにしたら動きました。


        projects: [
        {title: “Artwork”, image: “https://picsum.photos/g/200?image=122”, category: [‘ART’,’DOODLES’]},
        {title: “Charcoal”, image: “https://picsum.photos/g/200?image=116”, category: ‘ART’},
        {title: “Sketching”, image: “https://picsum.photos/g/200?image=121”, category: ‘DOODLES’},
        {title: “Acrillic”, image: “https://picsum.photos/g/200?image=133”, category: [‘ART’,’WORKSHOPS’]},
        :

          • aoki_monpro
          • 2019年 11月 14日 11:45am

          Masaさん、検証ありがとうございます!
          時間作って確かめてみます^^

  1. この記事へのトラックバックはありません。

CAPTCHA


最近の記事

アーカイブ

  1. オンライン教材

    【React】初心者向け講座をリリースしました【MUI】【Udemy】
  2. バックエンド

    【Laravel第4弾】Vue.js3(CompositionAPI+Scrip…
  3. オンライン教材

    【AWS】【初心者向け】インフラの基礎からわかる講座をリリースしました【Udem…
  4. オンライン教材

    【ChatGPT】エンジニア編をリリースしました
  5. オンライン教材

    ChatGPTをビジネス活用する講座をリリースしました【Udemy】
PAGE TOP
Ads Blocker Image Powered by Code Help Pro

広告ブロックを摘出しました!!

ブラウザ拡張を使用して広告をブロックしていることが摘出されました。

ブラウザの広告ブロッカーの機能を無効にするか、
当サイトのドメインをホワイトリストに追加し、「更新」をクリックして下さい。

あなたが広告をブロックする権利があるように、
当方も広告をブロックしている人にコンテンツを提供しない権利と自由があります。

Powered By
Best Wordpress Adblock Detecting Plugin | CHP Adblock