vueのhooperでスライドを実装する
qiitaを書いていたら思った以上の分量になったので、ブログにも投稿しておきます。
vueでスライドを実装するにはhooperというlibraryが便利でした。
https://github.com/baianat/hooper
前提
今回使用するのは、vue-cli4とhooperです。
まず、vue-cli4を使えるようにします。最新を入れればいいですが、この情報が古くなった場合、4です。次に、hooperをpackage.jsonに追加します。
$ yarn global add @vue/cli
$ vue create sample-vue-project
$ cd sample-vue-project
$ yarn add hooper
$ cat package.json
package.jsonはこんな感じです。コピーして、yarn install
してもいいです。これでyarn install
とかすると、依存関係がインストールできます。
{
"name": "sample-vue-project",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"core-js": "^3.6.4",
"hooper": "^0.3.4",
"vue": "^2.6.11"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.3.0",
"@vue/cli-plugin-eslint": "~4.3.0",
"@vue/cli-service": "~4.3.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"vue-template-compiler": "^2.6.11"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}
vue
まず、vueの使い方を簡単に説明します。
vueはsrc/main.js
とsrc/App.vue
,src/index.html
などを書いてbuildします。
$ yarn serve
$ yarn build
デフォルトでは、dist
にファイルが置かれます。なお、root pathはpublic
になっています。例えば、https://example.com/path/to/img.png
を使いたければ、public/path/to/img.png
にファイルを置いて、App.vueには/path/to/img.png
と記述します。
次に、vue-cliですが、env(環境変数)を使う際は、.env
にVUE_APP_XXX=10
などと書いて、App.vueなどにはprocess.env.VUE_APP_XXX
とすることで環境変数を使えます。
hooper
https://baianat.github.io/hooper/
docsのexampleがわかりやすいですね。最小構成は以下です。大体わかると思いますが、オプションなども用意されていますので、そのあたりは後述します。
// https://baianat.github.io/hooper/examples.html#default-example
<template>
<hooper>
<slide>
slide 1
</slide>
<slide>
slide 2
</slide>
<hooper-navigation slot="hooper-addons"></hooper-navigation>
</hooper>
</template>
<script>
import {
Hooper,
Slide,
Navigation as HooperNavigation
} from 'hooper';
export default {
components: {
Hooper,
Slide,
HooperNavigation
}
}
</script>
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App)
}).$mount('#app')
hooperでの実装例
私の場合は、画像をスライドさせているので以下のような感じになりました。後で個別に解説します。
<template>
<hooper :settings="hooperSettings">
<slide v-for="(n,index) of products" :key="n">
<img :src="'/manga/'+ (index) +'.png'" />
<div class="page_n">{{ n }}</div>
</slide>
<hooper-navigation slot="hooper-addons"></hooper-navigation>
<hooper-pagination slot="hooper-addons"></hooper-pagination>
<hooper-progress slot="hooper-addons"></hooper-progress>
</hooper>
</template>
<script>
import {
Hooper,
Slide,
Progress as HooperProgress,
Pagination as HooperPagination,
Navigation as HooperNavigation
} from 'hooper';
import 'hooper/dist/hooper.css';
export default {
name: 'App',
components: {
Hooper,
Slide,
HooperProgress,
HooperPagination,
HooperNavigation
},
data() {
return {
products: [...Array(Number(process.env.VUE_APP_PAGE)).keys()],
hooperSettings: {
itemsToShow: 1,
centerMode: true
}
};
}
};
</script>
<style>
.hooper {
height: 100%;
}
button.hooper-indicator {
background-color: #000;
}
img {
width:640px;
}
.page_n {
text-align: center;
height: 50px;
}
</style>
hooperのslideをloopで書く
わざわざslideを一つずつ用意するのは面倒ですので、通常はfor,bindなどでloop処理を書くことになると思います。あるいは配列を持ってくるなど。以下は必要な部分の記述です。
<template>
<hooper :settings="hooperSettings">
<slide v-for="(n,index) of products" :key="n">
<img :src="'/manga/'+ (index) +'.png'" />
<div class="page_n">{{ n }}</div>
</slide>
</hooper>
</template>
export default {
name: 'App',
components: {
Hooper
},
data() {
return {
products: [...Array(Number(process.env.VUE_APP_PAGE)).keys()]
};
}
};
何をしているのかというと、環境変数のVUE_APP_PAGE
からdata{products}
にページ数を入れます。文字列なので数字に変換、それをslideにてループ。
スライドするのは、対応した画像ファイル、/manga/0.png
,/manga/1.png
などを順番にスライドさせます。画像ファイルは、/public/manga/0.png
などの場所に置きます。
v-forは(n,index) of products
のように書きますが、(n,index)の部分は、n of products
でもいいです。indexにも数が入ってます。ここでn,
に続く記述はvueのオプション(vueが用意する変数)のようなものです。
hooperのoption
hooperには様々なbarなどが用意されています。基本的にはこんな感じで使います。
<template>
<hooper :settings="hooperSettings">
// 上の全体位置を示すバー
<hooper-navigation slot="hooper-addons"></hooper-navigation>
// ページ、戻る、進むのボタン
<hooper-pagination slot="hooper-addons"></hooper-pagination>
// 下の全体位置を示す個別ボタン
<hooper-progress slot="hooper-addons"></hooper-progress>
</hooper>
</template>
import {
Hooper,
Slide,
Progress as HooperProgress,
Pagination as HooperPagination,
Navigation as HooperNavigation
} from 'hooper';
hooperのslideにて指定コンテンツを使う
この場合のスライド出力は、0 - Foo, 1 - Barとなります。data{items}
を変更すればいいでしょう。vueのexampleを参考にしましょう。
https://jp.vuejs.org/v2/guide/list.html
<template>
<hooper :settings="hooperSettings">
<slide v-for="(item, index) in items">
{{ index }} - {{ item.message }}
</slide>
</hooper>
</template>
<script>
import {
Hooper,
Slide
} from 'hooper';
import 'hooper/dist/hooper.css';
export default {
name: 'App',
components: {
Hooper,
Slide
},
data() {
return {
items: [
{ message: 'Foo' },
{ message: 'Bar' }
]
};
}
};
</script>
gh-pages + hugoとの連携
build後のファイルはデフォルトでハッシュ値をつけるので、vue.config.js
で固定したあとに、gh-actionsを書いていきます。
module.exports = {
configureWebpack: {
output: {
filename: '[name].js',
chunkFilename: '[name].js'
}
},
css: {
extract: {
filename: '[name].css',
chunkFilename: '[name].css'
},
},
}
私の場合は、非常にシンプルに、こんな感じになります。これはhugoを使っている場合で少し特殊ですが、ようは、VUE_APP_XXXに自動で画像ファイル数を入れる処理を書いて、build後に出力される必要なファイルを必要な場所にコピーしています。もちろん、buildオプションを指定して直接置くようにしてもいいです。その場合、コピー処理は不要です。
run: |
echo VUE_APP_PAGE=`ls ./static/manga/*.png|wc -l` > .env
yarn build
cp -rf ./dist/*.js ./static/manga
cp -rf ./dist/*.css ./static/manga
cp -rf ./dist/*.map ./static/manga
私は、hugoを使っているので、以下のように構成しています。
---
title: "yui | MANGA"
type: manga
page_image : "https://syui.cf/icon/ai.png"
description: "惑星で暮らすドラゴンと少女のお話"
---
<div id=app></div>
<script src=/manga/chunk-vendors.js></script>
<script src=/manga/app.js></script>
{{ partial "head-blog.html" . }}
{{ partial "header.html" . }}
{{ partial "manga-css.html" . }}
<main>
{{ .Content }}
</main>
</div>
{{ partial "footer.html" . }}
</body>
</html>
<link href=/manga/app.css rel=preload as=style>
<link href=/manga/app.js rel=preload as=script>
<link href=/manga/chunk-vendors.css rel=preload as=style>
<link href=/manga/chunk-vendors.js rel=preload as=script>
<link href=/manga/chunk-vendors.css rel=stylesheet>
<link href=/manga/app.css rel=stylesheet>
つまり、yarn build
してできたdist/index.html
の内容をhugoの必要な場所に置き換えます。dist/index.html
は通常、buildしても内容が変わるものではありません。よって、hugoのほうに記述しても問題ないのです。