チュートリアル - Vue.jsでPlayCanvas製3Dモデルビュワーサイトを作る 3 / 4


[前回](https://qiita.com/sutobu000/items/25caba219b99440cd243)はPlayCanvasにDOMを追加するまでを行います。
今回はVue.jsを使ってモデルの切り替わりなどを実装してきます。


##Vue.jsで3Dモデルを切り替える
現在は3Dモデルが一つだけしか確認することができないので、複数の3Dモデルを追加します。
ヒエラルキーからEntityを追加していきます。

ヒエラルキー上部の[+]ボタンか右クリックからAdd Entity → Entityを選択します。
「New Entity」という名前で追加されたと思います。
Entitiesなど任意の名前に変更などして差別化します。
<img width="390" alt="FF5DE61966CEFE3A9EF8C3B975EF3C66.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109168/277a2767-f533-0da0-8578-d8352b22e0e6.png">

Entitiesの中にModelを追加します。
ヒエラルキーのModelをDrag&DropでEntitiesの中に追加します。
<img width="304" alt="349C24BFCC1590FD9F647DB8E1635BE6.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109168/98a7f14e-1c3c-30a3-6090-5a7c797930ea.png">

3Dモデルを複数使用したいため、先ほどのModelを複製、またはPrimitiveなBoxなどを追加します。

複製はEntityを選択して、ヒエラルキー上部のDuplicate Entityをクリック。
Primitiveを追加するにはAdd EntityからPrimitiveを選択します。

上記の操作を行うと以下のようになります。

<img width="370" alt="E7E882CB182B0D9C28A1E15B0AD9FE16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109168/784c4a56-fec8-38ff-6ae0-469127140d3c.png">


追加したモデルが重なり合っていますので、追加したEntities内のEntityのインペクターからEnabledのチェックを外し非表示にしておきます。
<img width="1177" alt="186CA2B76C6D962C83A1F0FAA34A5E5E.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109168/3046ba0b-063d-7586-74bb-c3eee5939522.png">


3Dモデルを追加できたら、次はVue.jsを使ってそれらを操作していきます。
Vue.jsを使うために、PlayCanvasにVue.jsのCDNを使います。
EXTERNAL SCRIPTSという項目がありますのでこちらから登録します。

CDN : https://cdn.jsdelivr.net/npm/vue

<img width="1050" alt="2CB0D3C88FDB9FF3D11497E3F963AC37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109168/1276cfb2-943f-66b4-2e95-90c85efc0ed9.png">

Vue.jsを書いていく前に、Vue.jsがわからない人のために簡単に説明をします。
Vue.jsはプログレッシブフレームワークと言われるライブラリとフレームワークの中間のもので、フレームワークとは異なり適所に少しずつVueを適用することができます。
詳しく知りたい方はVue.jsの公式ページから参照いただけたらと思います。
https://jp.vuejs.org/

Vueを使って切り替えるために、index.jsをいかに書き換えます。

[index.js]

```js
var Index = pc.createScript('index');

Index.attributes.add("baseHtml", {type:"asset", assetType:"html"}); // 登録したhtmlを取得
Index.attributes.add("target", {type:"entity"}); // 参照するEntityを登録

Index.prototype.initialize = function() {
    var self = this; // this書き換え
    var canvas = document.getElementsByTagName("canvas")[0]; // canvasを取得
    canvas.classList.add("pcCanvas"); // canvasにclass名を指定
    var wrapper = document.createElement("div"); // div作成
    wrapper.classList.add("wrapper"); // 作成したdivにwrapperというclass名を指定
    wrapper.innerHTML = self.baseHtml._resources[0]; // 事前に登録していたhtmlをwrapperに流し込み
    document.body.appendChild(wrapper); // bodyにwrapperを追加

    var nowEntity = ""; // どのEntityが選択されているか保管
    
    var app = new Vue({ // Vue呼び出し
        el: '#app', // id名がappの要素を参照
        data: { // 使用するdataを登録
            clickFlag: false, // クリックされたか
            entities: self.target.children // 属性で追加したEntityの子どもを参照
        },
        methods: { // イベントハンドラ作成
            onEntityClick: function(target,index) { // Entityを選択された時
                var v_self = this; // thisを保管

                if(nowEntity){
                  nowEntity.enabled = false; // 今表示されているEntityを非表示に
                }
                target.enabled = true; // 選択したEntityを表示
                nowEntity = target; // 選択したEntityを保管

            }
        }
    });
};
```

属性を追加したので、EntityのRootからindex.jsをParseします。
Parseしたら属性のtargetにEntitiesを登録します。
![9EE1754EB2FFDD846248065ABA4D3F7E.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109168/6bc9bfcc-3ffd-8053-b505-6eba3ce0de7c.png)

Vueを使ってイベントハンドラを作っていますので、これをHTML側で使用します。
index.htmlをいかに書き換えます。

[index.html]

```html
<div id="app">
    <header class="header">
        <nav>
            <ol>
                <li v-for="(entity,index) in entities" @click="onEntityClick(entity,index)" ><span>{{entity.name}}</span></li>
            </ol>
        </nav>
    </header>
    <main class="main">
        <div class="container">
            <section>content01</section>
            <section>content01</section>
            <section>content01</section>
        </div>
    </main>
</div>
```

ここで、index.jsで呼び出したVueのdataが確認できます。
entitiesというのが、index.jsで属性追加したEntityです。
それをv-forを使って一つずつ呼び出しています。
呼び出すli要素にはonEntityClickを@clickのクリックイベントで呼び出すように書き加えています。

これでLaunchを確認すると、先ほど設定した属性要素のEntitiesの子要素であるEntityの名前が並んでいます。
これらをクリックするとクリックしたモデルに変更されるのが確認できます。
<img width="603" alt="EFD619BA8AA8D149B421A6362044E895.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109168/45da3ab8-7415-e64b-49f7-fd115228b709.png">

EditorからEntitiesにEntityを追加すると、PlayCanvasのリアルタイムレンダリングによりLaunchをリロードしなくても追加されます。
試しにEntitiesにPrimitiveを追加してみるとわかりやすいでしょう。

---

Vueを使って3Dモデルの切り替えができましたが、
パッと切り替わってしまうので見応えがありません。
そこで、切り替わる際にアニメーションを入れたいと思います。







##切り替え時の動きを作る
Webでアニメーションといえばライブラリが数多く用意されています。
PlayCanvasにもTweenライブラリが用意されていて、これを使うことで現在の値から指定した値へアニメーションを行うことができます。

PlayCanvas Tween Library
https://developer.playcanvas.com/en/tutorials/tweening/



新しくtween.jsを作り、このTween Libraryのコードをコピペします。
書き換え終えたらOKです。
jsのインスペクターを見るとpreloadにチェックが入っています。
これにチェックを入れているとアプリケーションが読み込まれた時に一度ロードされます。
PlayCanvasに関わる記述以外を一度読み込みます。tween.jsはPlayCanvasに関わる記述がないために、Entityへの登録は必要が無くなります。

![C51A4EC3EE5B960F9174B7735738EF0F.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109168/373dbd04-3a38-d065-e07f-5d20f5d66cbf.png)

では、追加したTween Libraryを使ってアニメーションを追加していきます。
index.jsのVueの箇所を以下のコードに書き換えてください。

[index.js]

```js
var Index = pc.createScript('index');

Index.attributes.add("baseHtml", {type:"asset", assetType:"html"}); // 登録したhtmlを取得
Index.attributes.add("target", {type:"entity"}); // 参照するEntityを登録

Index.prototype.initialize = function() {
    var self = this; // this書き換え
    var canvas = document.getElementsByTagName("canvas")[0]; // canvasを取得
    canvas.classList.add("pcCanvas"); // canvasにclass名を指定
    var wrapper = document.createElement("div"); // div作成
    wrapper.classList.add("wrapper"); // 作成したdivにwrapperというclass名を指定
    wrapper.innerHTML = self.baseHtml._resources[0]; // 事前に登録していたhtmlをwrapperに流し込み
    document.body.appendChild(wrapper); // bodyにwrapperを追加

    var nowEntity = ""; // どのEntityが選択されているか保管
    
    var app = new Vue({ // Vue呼び出し
        el: '#app', // id名がappの要素を参照
        data: { // 使用するdataを登録
            clickFlag: false, // クリックされたか
            entities: self.target.children // 属性で追加したEntityの子どもを参照
        },
        methods: { // イベントハンドラ作成
            onEntityClick: function(target,index) { // Entityを選択された時
                var v_self = this; // thisを保管

                if(v_self.clickFlag) return; // クリックされて処理中か
                v_self.clickFlag = true; // クリック処理中
                
                self.target.tween(self.target.getLocalRotation()).rotate(new pc.Vec3(180, 0, 0), 0.5, pc.QuadraticIn).on('complete', function () { // tweenでrotateをアニメーション completeで完了後呼び出し
                    if(nowEntity){ // Entityが保管されているか
                        nowEntity.enabled = false;
                    }
                    target.enabled = true; // 選択したEntityを表示
                    nowEntity = target; // 選択したEntityを保管

                    self.target.tween(new pc.Vec3(180, 0, 0)).rotate(new pc.Vec3(360, 0, 0), 1, pc.BounceOut).on('complete', function(){ v_self.clickFlag = false; }).start(); // 選択したEntityのアニメーション
                }).start();
                
            }
        }
    });
};
```

今回はTweenを使ってRotateさせて180度開店したら選択したEntityに変わり、また180度回転して元に戻るようにしています。

このTweenのアニメーションはPositionやScale、マテリアルがあればその値も使うことができるので、色んな場で活用できる便利なライブラリです。


ここまで出来たら、せっかくDOMも追加してVueも入れているので
選択した3Dモデルの詳細スペックなどが見れるように新たにDOMが表示されるようにしたいと思います。