ViteでQRリーダーアプリを作る
はじめに
こちらの記事で既にQRリーダーアプリの作り方は載っていますが、Vue.jsで作りたかったのでそれで作った時の内容をまとめたいと思います。またせっかくなので最近流行りのViteで環境構築し、それをGitHub Pagesで公開しました。
ViteでVue.js 3が動くようにする
Viteでの環境構築はかなり楽で、公式サイトに書かれている通りにすると簡単に構築できます。
$ yarn create @vitejs/app my-vue-app --template vue-ts
追加で僕はpugとsassを使いたかったのでyarn addします。
$ yarn add --dev pug sass
QRリーダーアプリの実装
最初に共有した記事を参考に、QRリーダーコンポーネントを実装します。
<template lang="pug">
div
video.video(ref="elVideoRef")
</template>
<script lang="ts">
import { defineComponent, ref, onMounted, onBeforeUnmount } from 'vue';
import jsQR from 'jsqr';
/** スキャンする間隔 */
const INTERVAL = 200;
export default defineComponent({
name: 'QRReader',
emits: {
scan: (code: string) => {
return !!code;
},
},
setup(props, context) {
let intervalId: number;
const elVideoRef = ref<HTMLVideoElement>();
const elInternalCanvas = document.createElement('canvas');
const scanQRCode = () => {
const elVideo = elVideoRef.value;
if (!elVideo) {
return;
}
const ctx = elInternalCanvas.getContext('2d');
if (ctx == null) {
return;
}
ctx.drawImage(elVideo, 0, 0, elInternalCanvas.width, elInternalCanvas.height);
const imageData = ctx.getImageData(0, 0, elInternalCanvas.width, elInternalCanvas.height);
const code = jsQR(imageData.data, elInternalCanvas.width, elInternalCanvas.height);
if (code) {
context.emit('scan', code.data);
}
};
onMounted(async () => {
if (!elVideoRef.value) {
return;
}
const elVideo = elVideoRef.value;
elVideo.srcObject = await navigator.mediaDevices.getUserMedia({
audio: false,
video: {
facingMode: 'environment',
},
});
elVideo.setAttribute('playsinline', 'true');
elVideo.onloadedmetadata = () => {
elVideo.play();
console.log(`video resolution: ${elVideo.videoWidth} x ${elVideo.videoHeight}`);
elInternalCanvas.width = elVideo.videoWidth;
elInternalCanvas.height = elVideo.videoHeight;
};
intervalId = setInterval(() => {
scanQRCode();
}, INTERVAL);
});
onBeforeUnmount(() => {
clearInterval(intervalId);
elInternalCanvas.remove();
if (elVideoRef.value && elVideoRef.value.srcObject) {
if ('getVideoTracks' in elVideoRef.value.srcObject) {
elVideoRef.value.srcObject.getVideoTracks()[0].stop();
}
}
});
return {
elVideoRef,
};
},
});
</script>
<style lang="scss" scoped>
.video {
width: 100%;
}
</style>
コンポーネントができたら後は呼び出して表示するだけです。
<template lang="pug">
div
.box
QRReader(
@scan="onScan"
)
.info
div QRデータ:
template(v-if="isUrl")
a(:href="state.qrCode", target="_blank") {{ state.qrCode }}
template(v-else)
div {{ state.qrCode }}
</template>
<script lang="ts">
import { defineComponent, reactive, computed } from 'vue';
import QRReader from './components/QRReader.vue';
interface IState {
/** QRコードデータ */
qrCode: string;
}
export default defineComponent({
name: 'App',
components: {
QRReader,
},
setup() {
const state = reactive<IState>({
qrCode: '',
});
const isUrl = computed(() => {
return /^https?:\/\//.test(state.qrCode);
});
return {
state,
isUrl,
onScan: (code: string) => {
console.log(code);
state.qrCode = code;
},
};
},
});
</script>
<style lang="scss" scoped>
.box {
max-width: 600px;
margin: 0 auto;
}
.info {
word-break: break-all;
}
</style>
PWA化する
最後にPWA化します。まずviteにはvite-plugin-pwa
があるのでそれをinstallします。
$ yarn add --dev vite-plugin-pwa
manifest.jsonの設定
そしてvite.config.ts
にPWAの設定を追記します。
GitHub Pagesではサブディレクトリに成果物が配置されるため、その辺を考慮して設定する必要があります。
iconファイルは自動で配置する方法が分からなかったので、public/app-icons
にファイルを配置し、ビルド後の出力パスと合うようにsrc
を指定しています。
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { VitePWA } from 'vite-plugin-pwa'
// https://vitejs.dev/config/
export default defineConfig({
// GitHub Pagesはサブディレクトリに配置されるため相対パスでファイルアクセスする
base: './',
plugins: [
vue(),
VitePWA({
manifest: {
lang: 'ja',
name: 'QR Reader',
short_name: 'QR',
background_color: '#fff',
theme_color: '#3cb371',
display: 'standalone',
// GitHub Pagesにpushする場合はリポジトリ名を入れる必要がある
scope: '/repository-name/',
start_url: '/repository-name/',
icons: [
{
src: 'app-icons/72x72.png',
sizes: '72x72',
type: 'image/png'
},
// 他のサイズも入れる
]
}
})
]
})
参考
service workerの登録
上の設定に加えて、service workerも起動するように設定する必要があります。
main.ts
でregisterSW
を実行します。
import { createApp } from 'vue'
import App from './App.vue'
// service workerの登録
import { registerSW } from 'virtual:pwa-register';
registerSW();
createApp(App).mount('#app')
ただここで注意なのが、virtual:pwa-register
というモジュールの型情報が入っていないためTypeScriptだとエラーになってしまいます。tsconfig.json
のincludes
に型情報を追加します。
{
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue",
// ↓追加する
"node_modules/vite-plugin-pwa/client.d.ts"
]
}
参考
GitHub Pagesにデプロイする
後はGitHub Actionsでgh-pagesブランチにpushしたら完了です。
name: github pages
on:
push:
branches:
- main
jobs:
build-deploy:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@master
- name: Setup
uses: actions/setup-node@v1
with:
node-version: '12.16.x'
- name: Install
run: yarn install
- name: Build
run: yarn build
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./dist
終わりに
以上がViteを使ってQRリーダーアプリを作る流れでした。
ソースは以下に置いていますので、興味がある方は見てください。
Discussion