1
0
hugo/content/blog/2020-04-11-vue.md
2024-12-21 21:03:10 +09:00

378 lines
9.7 KiB
Markdown

+++
date = "2020-04-11"
tags = ["vue"]
title = "vueのhooperでスライドを実装する"
slug = "vue"
+++
qiitaを書いていたら思った以上の分量になったので、ブログにも投稿しておきます。
vueでスライドを実装するにはhooperというlibraryが便利でした。
https://github.com/baianat/hooper
### 前提
今回使用するのは、vue-cli4とhooperです。
まず、vue-cli4を使えるようにします。最新を入れればいいですが、この情報が古くなった場合、4です。次に、hooperをpackage.jsonに追加します。
```sh
$ 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`とかすると、依存関係がインストールできます。
```json:package.json
{
"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します。
```sh
$ 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がわかりやすいですね。最小構成は以下です。大体わかると思いますが、オプションなども用意されていますので、そのあたりは後述します。
```html:src/App.vue
// 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>
```
```js:src/mani.js
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
new Vue({
render: h => h(App)
}).$mount('#app')
```
#### hooperでの実装例
私の場合は、画像をスライドさせているので以下のような感じになりました。後で個別に解説します。
```html:src/App.vue
<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処理を書くことになると思います。あるいは配列を持ってくるなど。以下は必要な部分の記述です。
```html:src/App.vue
<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などが用意されています。基本的にはこんな感じで使います。
```html:src/App.vue
<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
```html:App.vue
<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を書いていきます。
```js:vue.config.js
module.exports = {
configureWebpack: {
output: {
filename: '[name].js',
chunkFilename: '[name].js'
}
},
css: {
extract: {
filename: '[name].css',
chunkFilename: '[name].css'
},
},
}
```
私の場合は、非常にシンプルに、こんな感じになります。これはhugoを使っている場合で少し特殊ですが、ようは、VUE_APP_XXXに自動で画像ファイル数を入れる処理を書いて、build後に出力される必要なファイルを必要な場所にコピーしています。もちろん、buildオプションを指定して直接置くようにしてもいいです。その場合、コピー処理は不要です。
```yml:.github/workflows/gh-pages.yml
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を使っているので、以下のように構成しています。
```md:content/manga.md
---
title: "yui | MANGA"
type: manga
page_image : "https://syui.ai/icon/ai.png"
description: "惑星で暮らすドラゴンと少女のお話"
---
<div id=app></div>
<script src=/manga/chunk-vendors.js></script>
<script src=/manga/app.js></script>
```
```html:layouts/manga/single.html
{{ partial "head-blog.html" . }}
{{ partial "header.html" . }}
{{ partial "manga-css.html" . }}
<main>
{{ .Content }}
</main>
</div>
{{ partial "footer.html" . }}
</body>
</html>
```
```html:layouts/partials/manga-css.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のほうに記述しても問題ないのです。