Vite + Vue3 + Electron + PixiJS

# はじめに
Vite + Vue3 + Electron や、Electron + Vue3 + PixiJS、といった組み合わせのサンプルはあったのですが、Vite + Vue3 + Electron + PixiJS の組み合わせのサンプルが見つからなかったので、設定の備忘録用です
なお、IDEは[JetBrains社](https://www.jetbrains.com/ja-jp/)の、[WebStorm](https://www.jetbrains.com/ja-jp/webstorm/)を使っています
# 目次
1. [環境](#1-環境)
1. [electron-viteのインストール](#2-electron-viteのインストール)
1. [PixiJSのインストール](#3-pixijsのインストール)
1. [PixiJSの動作確認](#4-pixijsの動作確認)
1. [@pixi/unsafe-evalのインストール](#5-pixiunsafe-eval-のインストール)
1. [アプリケーションのビルド](#6-アプリケーションのビルド)
1. [StaticPathを取得して、画像を表示する](#7-static-pathを取得して画像を表示する)
1. [参考URL](#8-参考url)
# 1. 環境
- WebStorm 2023.3.2
- Node 18.18.2
- Vue 3.4.5
- Vite 5.0.11
- PixiJS 7.3.3
- Windows 11 Home
# 2. electron-viteのインストール
まずは、PixiJS以外を使えるようにします
WebStorm で空のプロジェクトを作ります
プロジェクト名は「vite_vue3_pixi」としています
![スクリーンショット 2024-01-17 114450.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49845/04de754a-97ae-a9d4-7192-c39fa1409f60.png)
ターミナルから、ディレクトリを移動して、`electron-vite`をインストールします
```terminal:Terminal
cd ../
npm create @quick-start/electron
```
![スクリーンショット 2024-01-17 115500.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49845/16fd1e1f-6a85-87f5-2ac2-9f1b8bef21af.png)
`electron-vite` のメッセージに従い、インストールを進めます
| メッセージ | 説明 | 値 |
|:-----------|:------------|:----|
| Project name | インストール先のディレクトリ|vite_vue3_pixi|
| Target directory "vite_vue3_pixi" is not empty. Remove existing files and continue ?|インストール先が空じゃないけど、既に入ってるファイルは消す？|yes
| Select a framework | vanilla<br>vue<br>react<br>svelte<br>solid|vue|
| Add TypeScript | TypeScriptを追加しますか？ | yes |
| Add Electron updater plugin ? | Electron アップデータプラグインを追加しますか？ | yes |
|Enable Electron download mirror proxy ? | ミラーサイトからElectronをダウンロードしますか？ | yes |

インストール先のディレクトリに移動して、インストールを行います
```terminal:Terminal
cd vite_vue3_pixi
npm install
```

インストール後、`package.json`の`"dev":electron-vite dev`の左側にある再生ボタンを押して、`'dev'の実行`を選択し、動作確認を行います
![スクリーンショット 2024-01-17 115925.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49845/f8ea5001-440f-0d17-88b5-bb880de51c06.png)
実行すると以下の画面が出ます
![スクリーンショット 2024-01-17 120022.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49845/68f4694f-65fb-3611-2f98-4529511cca05.png)
これで、Vite、Vue3、Electron、はインストールされました
# 3. PixiJSのインストール
次にPixiJSをインストールしていきます
ターミナルでPixiJSをインストールします
```terminal:Terminal
npm install pixi.js
```
# 4. PixiJSの動作確認
## 4-1. サンプルファイルの作成
Pixiのテスト用に`components`ディレクトリに`PixiComponent.vue`という名前でVueファイルを作成します
作成したファイルに、PixiJSを表示するコードを記述します
このコードは、PixiJSアプリを作成（`CreatePixiApp()`）して、画像をスプライトに読み込んで（`PIXI.Sprite.from`）、ステージに配置（`pixiApp.stage.addChild()`）して、回転（`pixiApp.ticker.add()`）しているサンプルになります
```vue:components/PixiComponent.vue
<script setup lang="ts">
import * as PIXI from 'pixi.js'
import { onMounted, ref } from 'vue'
const cv = ref()
const createPixiApp = (_canvas: HTMLCanvasElement) => {
  const canvas = _canvas
  canvas.getContext('webgl', {
    preserveDrawingBuffer: true,
    stencil: true
  })
  return new PIXI.Application({
    antialias: true,
    autoDensity: true,
    resizeTo: window,
    view: canvas,
    backgroundColor: 0x000000
  })
}
onMounted(() => {
  const pixiApp = createPixiApp(cv.value)
  const sprite = PIXI.Sprite.from('./src/assets/icon.png')
  sprite.anchor.set(0.5)
  pixiApp.stage.addChild(sprite)
  pixiApp.ticker.add((delta: number) => {
    sprite.rotation += delta * 0.01
    sprite.x = pixiApp.screen.width / 2
    sprite.y = pixiApp.screen.height / 2
  })
})
</script>
<template>
  <div id="pixi-wrapper">
    <canvas id="pixi-container" ref="cv"></canvas>
  </div>
</template>

<style scoped lang="less">
#pixi-wrapper,
#pixi-container {
  width: 100%;
  height: 100%;
}
</style>
```
`./src/assets/icon.png`という画像を読み込んでいる箇所がありますが、
`resources`ディレクトリ内に同名の画像ファイルがありますので、今回はこのファイルをコピーして使っています
## 4-2. PixiComponent.vueの読み込み
先ほど作った`PixiComponent.vue`を読み込んで表示したいと思います
`App.vue`を以下に変更します
```vue:src/App.vue
<script setup lang="ts">
import PixiComponent from './components/PixiComponent.vue'
</script>

<template>
  <pixi-component></pixi-component>
</template>

<style lang="less">
</style>
```
## 4-3. 実行・デバッグ
`'dev'の実行`を選択し、動作確認を行います
![スクリーンショット 2024-01-18 092546.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49845/388663c8-5f30-795d-1846-f9478cebd726.png)
`PIXI.Application`の背景色（backgraoundColor）を黒（0x000000）にしているので、黒い画面に画像が表示される想定ですが、画面が真っ白です
## 4-4. メニューとデベロッパーツールの表示
画面が真っ白な原因を確かめるために、`src/main/index.ts`ファイルを開き、Electron側のメニューとデベロッパーツールを表示するコードを追加します
```typescript:src/main/index.ts
/*
　省略
*/
function createWindow(): void {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: 900,
    height: 670,
    show: false,
    autoHideMenuBar: false, /* true -> false に変更 */
    ...(process.platform === 'linux' ? { icon } : {}),
    webPreferences: {
      preload: join(__dirname, '../preload/index.js'),
      sandbox: false
    }
  })
/*
　省略
*/
  if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
    mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
    mainWindow.webContents.openDevTools() /* <----- この行を追加 */
  } else {
    mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
  }
}
/*
　省略
*/
```
再度、`'dev'の実行`を選択し、動作確認を行います
![スクリーンショット 2024-01-18 092852.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49845/93eab9d5-b52a-5b44-77e2-402cf8476c8e.png)
メニューとデベロッパーツールが表示され、エラーが表示されているのが確認できます
エラー内容は、
```console:Console
Error : Current enviroment does not allow unsafe-eval, please use @pixi/unsafe-eval module to enable support.
```
というもので、「`unsafe-eval`が使えないから、モジュールを入れてね」みたいなエラーのようなので、このモジュールを追加します
※ただ、`unsafe-eval`はセキュリティ的に無効になっているものを有効？にして使うようなので、自己責任で設定してください
# 5. @pixi/unsafe-eval のインストール
ターミナルから@pixi/unsafe-evalをインストールします
```terminal:Terminal
npm install @pixi/unsafe-eval
```
さらに再度、`'dev'の実行`を選択し、動作確認を行います
![スクリーンショット 2024-01-22 121050.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49845/b5e41c7b-5d7a-cc72-7672-1b6acbbea0c6.png)
無事、画像が回転しているコンテンツが実行されました
# 6. アプリケーションのビルド
コンテンツが表示されましたので、実際にアプリケーションをビルドしてみます
Electron の書き出し設定は、`electron-builder.yml`ファイルに記載されています
今回はこのままデフォルト設定でビルドしてみます
`package.json`ファイルを開き、僕の環境はWindowsですので、`build:win` を実行します
![スクリーンショット 2024-01-23 171146.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49845/2edff83d-7f40-79bb-723c-8b77f433e0b0.png)
ビルドが完了すると、`dist`ディレクトリにファイルが生成されます
`dist/win-unpacked`ディレクトリをエクスプローラから開き、`vite_vue3_pixi.exe`を起動します
![スクリーンショット 2024-01-23 191954.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49845/fb66fe1e-415e-cfbb-21aa-3af7d7013427.png)
すると、画面が真っ暗で、画像が表示されません
ということで、デベロッパーツールを開いてエラーを確認します
![スクリーンショット 2024-01-23 195448.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49845/0ed3f4f6-6587-2840-3242-ccb325e3e781.png)
```console:Console
Failed to load resource: net::ERR_FILE_NOT_FOUND   icon.png:1
```
というエラーで、どうやら画像のパスが正しくないようです
ビルドした際、パスが変わってしまう事が原因のようなので、ビルドしても変換してくれるように、
`PixiComponent.vue`のコードを変更します

# 7. Static Pathを取得して、画像を表示する
`import.meta.url`を使うと、画像の相対パスを、正しいパスに変換してくれますので、このモジュールを使い、パスの設定を行います
```vue:components/PixiComponent.vue
<script setup lang="ts">
/*
省略
*/
const cv = ref()
const imgUrl = new URL('../assets/icon.png', import.meta.url).href  //<--- 追加
const createPixiApp = (_canvas: HTMLCanvasElement) => {
/*
省略
*/
}
onMounted(() => {
  const pixiApp = createPixiApp(cv.value)
  const sprite = PIXI.Sprite.from(imgUrl)  //<---変更
/*
省略
*/
}
</script>

<template>
  <div id="pixi-wrapper">
    <canvas id="pixi-container" ref="cv"></canvas>
  </div>
</template>

<style scoped lang="less">
#pixi-wrapper,
#pixi-container {
  width: 100%;
  height: 100%;
}
</style>
```
再度ビルドし直して、`vite_vue3_pixi.exe`を実行してみると、問題なく表示することが出来ました
![スクリーンショット 2024-01-22 121050.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49845/b5e41c7b-5d7a-cc72-7672-1b6acbbea0c6.png)
このモジュールを使うと、開発時でもパスを適宜正しいパスに変換してくれるので、関数を作っておくと良いかと思います
```vue:components/PixiComponent.vue
<script setup lang="ts">
/*
省略
*/
//const imgUrl = new URL('../assets/icon.png', import.meta.url).href
const imgUrl = (_filename: string): string => {
  return new URL(`../assets/${_filename}`, import.meta.url).href
}
/*
省略
*/
//const sprite = PIXI.Sprite.from(imgUrl)
  const sprite = PIXI.Sprite.from(imgUrl('icon.png'))
</script>
```
ただし、画像が入っているディレクトリは静的に記述しないと、正しく変換されないようですので注意が必要です
```typescript
//これは変換されない
const imgUrl = (_filename: string): string => {
  return new URL(_filename, import.meta.url).href
}
```
# 8. 参考URL
- [Electron-Vite](https://electron-vite.github.io/)
- [静的アセットの取り扱い](https://ja.vitejs.dev/guide/assets.html)
- [electron-vite で Electron アプリ開発の生産性を上げる](https://developer.mamezou-tech.com/blogs/2023/05/22/electron-vite/)