ログインしたあなたに贈るクリスマスツリー


## やったこと

認証機能を持つサイトを作り、ログインするとクリスマスツリーが表示されるサイトを作りました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/d218a8df-8ff3-c761-b680-ae79b33c6908.png" alt="クリスマスツリー" width="450" border="0" />

## できたもの

- ログインするとクリスマスツリーが表示されます。
- ~~ホントはツリーに、ログインユーザーのアカウント名を表示したかったのですが出来ませんでした。~~
- 2019/12/13 追記
    - ログインユーザーのアカウント名表示できました。アカウント名を取得して3Dオブジェクトとしてツリーの隣に配置しました。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ちょっと改良。ツリーの隣にログオンユーザーのアカウント名（JjjJ j4amountain）と、Merry Christmasの文字を追加 <a href="https://t.co/Dslqfa3756">pic.twitter.com/Dslqfa3756</a></p>&mdash; j4amountain (@zsipparu) <a href="https://twitter.com/zsipparu/status/1205275638652735489?ref_src=twsrc%5Etfw">December 12, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>





## 参考

- [超初心者がAuth０でログイン機能を実装した](https://qiita.com/marumaruchan/items/552cc7b679c06bc66d53)
    - Auth0を使用したシングルページの認証サイトの作成方法を参考にさせて頂きました。
- [WebVRフレームワークA-Frameで、雪降る街の景色を作ってみました](https://qiita.com/tspringkj/items/9c2645f745a02b47a0a8)
    - クリスマスツリーのソースをお借りしました。

## ソース

- Auth0のサンプルを改良したindex.htmlファイルをgithubに置きました。
- https://github.com/zgw426/Christmastree2019
    - index-r1.html : クリスマスツリーのみ
    - index-r2.html : ＋アカウント名

## 作り方

[Auth0](https://auth0.com/jp/)でシングルページの認証サイトのアプリケーション登録し、登録したアプリケーションの`Quick Start`にあるサンプルをダウンロードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274270/4311385c-c297-372d-611e-027987b4c302.png)

ダウンロードしたサンプルにある`index.html`ファイルを更新して、クリスマスツリーを追加しました。クリスマスツリーは、`A-Frame`というhtmlで3Dオブジェクトを生成できるスクリプトを使用しました。

#### 1.クリスマスツリーの追加 

作成したindex.htmlは、[github](https://github.com/zgw426/Christmastree2019)の`index-r1.html`です。

ここでは、ベースのindex.htmlからの変更点を紹介します。

###### 1-1.A-Frameのモジュールをロード処理を追加

A-Frameのモジュール`aframe.min.js`をロードします。

```html:index-r1.html(抜粋:モジュールロード部分)
<script src="https://aframe.io/releases/0.8.0/aframe.min.js"></script>
```

別バージョンのモジュールをロードすると正常に処理が実行されない場合があるので注意ください。以下のモジュールを指定した場合、カメラワークの処理（カメラがツリーの周りをまわる）が停止しました。

```html:(こっちはカメラワーク処理が停止した)
<script src="https://aframe.io/releases/0.9.2/aframe.min.js"></script>
```

###### 1-2.3Dオブジェクトを追加

A-Frameは、A-Frameが用意したタグを`<a-scene>・・・</a-scene>`の間(・・・のところ)に記載することで3Dオブジェクトが描画されます。簡単ですね。（`<a-scene>`もA-Frameのタグです）

```html:index-r1.html(抜粋:3Dオブジェクト部分)
<a-scene id="viewport"
        data-range="60 8 60">
<a-entity>
  <a-animation attribute="rotation"
              from="0 0 0" to="0 360 0"
              dur="72000" easing="linear"
              repeat="indefinite"
              >    
  </a-animation>  


<a-plane id="ground"
rotation="-90 0 0"
width="200" height="200"
material="shader: flat; color: #f0f0f0"
>
</a-plane>

<a-sky id="sky" color="#040d1a"></a-sky>  

<a-entity id="tree_christmas" scale="4 4 4">
  <a-animation attribute="rotation"
              from="0 360 0" to="0 0 0"
              dur="72000" easing="linear"
              repeat="indefinite"
              >    
  </a-animation>
<a-entity id="tree_xmas_parts"
position="0 1 0">

<a-entity id="xmas_parts1">
<a-box material="color:#4cca10" geometry="" position="-0.04 1.374 -0.284" scale="1 0.793 1"></a-box><a-box material="color:#4cca10" position="0.044 0.844 0.53" scale="1 0.43 1"></a-box><a-box material="color:#4cca10" position="-0.876 0.932 -0.275" scale="1.172 0.556 1" geometry=""></a-box><a-box material="color:#4cca10" position="-1.278 0.527 -0.275" scale="1.172 0.556 1"></a-box><a-box material="color:#4cca10" position="1.016 1.023 0" scale="1.063 0.59 1" geometry=""></a-box><a-box material="color:#4cca10" position="0.361 0.361 -0.47" scale="1.561 0.491 1"></a-box><a-box material="color:#4cca10" position="0.085 0.841 -0.977" scale="1.561 0.575 1"></a-box>
<a-box id="node1" material="color:#a44523"></a-box>
</a-entity>

<a-entity id="xmas_parts2" position="-0.456 1.709 -0.266" rotation="0 -156.704 0">
    <a-box material="color:#4cca10" position="-0.216 1.161 -0.359" scale="1 0.618 0.773"></a-box><a-box material="color:#4cca10" position="0.044 0.844 0.53" scale="1 0.43 1"></a-box><a-box material="color:#4cca10" position="-0.876 0.675 -0.275" scale="1.172 0.556 1"></a-box><a-box material="color:#4cca10" position="-1.064 0.527 -0.722" scale="1.172 0.556 1" geometry=""></a-box><a-box material="color:#4cca10" position="-0.031 0.205 -0.686" scale="1.172 0.556 1" rotation="0 -39.99245410013146 0"></a-box><a-box material="color:#4cca10" position="0.697 1.023 -0.137" scale="1.063 0.59 1"></a-box><a-box material="color:#4cca10" position="0.6 0.361 0.6" scale="1.231 0.491 1" rotation="0 -40.90918657234078 0"></a-box><a-box material="color:#4cca10" position="0.761 0.406 -0.435" scale="1.231 0.491 1"></a-box><a-box material="color:#4cca10" position="0.451 -0.396 -0.942" scale="1.231 0.491 1"></a-box><a-box material="color:#4cca10" position="-0.579 1.237 0.567" scale="0.894 0.329 0.573"></a-box>
    <a-box id="node2" material="color:#a44523" position="-0.181 0 -0.28" rotation="0 -13.349916626548183 0" scale="0.912 1.313 1"></a-box>
</a-entity>

    <a-entity id="xmas_parts3" position="-0.456 3.408 -0.345" rotation="0 -172.575 0">
        <a-box material="color:#4cca10" position="-0.26 1.374 -0.278" scale="0.613 0.631 0.432" geometry=""></a-box><a-box material="color:#4cca10" position="0.223 1.023 -0.532" scale="0.613 0.631 0.432"></a-box><a-box material="color:#4cca10" position="0.319 0.844 0.168" scale="0.656 0.43 0.514" rotation="0 -29.621918008263563 0"></a-box><a-box material="color:#4cca10" position="-0.224 0.932 -0.029" scale="0.761 0.556 0.609" rotation="0 -21.772396214971284 0" geometry=""></a-box><a-box material="color:#4cca10" position="-0.462 0.932 -0.665" scale="0.616 0.456 0.609" rotation="0 -124.15995420484938 0"></a-box><a-box material="color:#4cca10" position="-0.905 0.527 -0.555" scale="0.897 0.556 0.665" geometry=""></a-box><a-box material="color:#4cca10" position="-0.456 0.209 0.013" scale="0.897 0.43 0.665"></a-box><a-box material="color:#4cca10" position="0.382 0.722 -0.373" scale="1.063 0.411 0.719"></a-box><a-box material="color:#4cca10" position="0.775 0.376 -0.34" scale="0.897 0.381 0.517" geometry=""></a-box><a-box material="color:#4cca10" position="0.413 -0.004 0.14" scale="0.73 0.381 0.866" rotation="0 -45.664736271926614 0"></a-box><a-box material="color:#4cca10" position="0.403 0.227 -0.787" scale="0.897 0.381 0.517" rotation="0 31.168904055116787 0"></a-box><a-box material="color:#4cca10" position="-0.319 0.227 -0.881" scale="0.897 0.381 0.517" rotation="0 131.89488443911551 0"></a-box><a-box material="color:#4cca10" position="-0.345 0.841 0.233" scale="0.712 0.329 0.86"></a-box>
        <a-box id="node3" material="color:#a44523" position="-0.181 -0.062 -0.317" scale="0.596 1.472 0.455"></a-box>
    </a-entity>

    <a-entity id="xmas_parts4" position="-0.031 4.738 -0.294" rotation="0 90 0" scale="0.808 0.808 0.808">
        <a-box material="color:#4cca10" position="-0.098 1.374 -0.278" scale="0.613 0.631 0.432"></a-box><a-box material="color:#4cca10" position="0.223 1.023 -0.532" scale="0.613 0.631 0.432"></a-box><a-box material="color:#4cca10" position="0.319 0.844 0.168" scale="0.656 0.43 0.514" rotation="0 -29.621918008263563 0"></a-box><a-box material="color:#4cca10" position="-0.224 0.932 -0.029" scale="0.761 0.556 0.609" rotation="0 -21.772396214971284 0"></a-box><a-box material="color:#4cca10" position="-0.462 0.932 -0.665" scale="0.616 0.456 0.609" rotation="0 -124.15995420484938 0"></a-box><a-box material="color:#4cca10" position="-0.68 0.527 -0.555" scale="0.897 0.556 0.665"></a-box><a-box material="color:#4cca10" position="-0.456 0.209 0.013" scale="0.897 0.43 0.665"></a-box><a-box material="color:#4cca10" position="0.382 0.722 -0.373" scale="1.063 0.411 0.719"></a-box><a-box material="color:#4cca10" position="0.775 0.376 -0.34" scale="0.897 0.381 0.517"></a-box><a-box material="color:#4cca10" position="0.413 -0.004 0.14" scale="0.73 0.381 0.866" rotation="0 -45.664736271926614 0"></a-box><a-box material="color:#4cca10" position="0.403 0.227 -0.787" scale="0.897 0.381 0.517" rotation="0 31.168904055116787 0"></a-box><a-box material="color:#4cca10" position="-0.319 0.227 -0.881" scale="0.897 0.381 0.517" rotation="0 131.89488443911551 0"></a-box><a-box material="color:#4cca10" position="-0.345 0.841 0.233" scale="0.712 0.329 0.86"></a-box>
            <a-box id="node4" material="color:#a44523" position="-0.181 -0.062 -0.317" scale="0.596 1.472 0.455"></a-box>
        </a-entity>
</a-entity>

<!-- tree light top -->
<a-entity id="tree_light_top">
    <a-entity position="-0.234 7.48 -0.175" scale="0.378 0.378 0.378" geometry="primitive:octahedron" material="emissive:#c90;color:#fff"></a-entity>

</a-entity>

<!-- tree blinks -->
<a-entity id="tree_blinks">
        <a-entity id="tree_blink" position="-1.123 3.604 0.827" light="color:#F00;type:point">
            <a-animation attribute="light.intensity" from="0.5" to="1.5" dur="2000" delay="0" direction="alternate" repeat="indefinite"></a-animation>
        </a-entity>

        <a-entity id="tree_blink-2" position="-0.263 5.522 1" light="color:#00f;intensity:3;groundColor:#fff;type:point">
            <a-animation attribute="light.intensity" from="0.5" to="1.5" dur="2000" delay="200" direction="alternate" repeat="indefinite"></a-animation>
        </a-entity>

        <a-entity id="tree_blink-3" position="0.846 2.515 0.867" light="intensity:2.083;color:#ff0;groundColor:#fff;type:point">
            <a-animation attribute="light.intensity" from="0.5" to="1.5" dur="2000" delay="400" direction="alternate" repeat="indefinite"></a-animation>
        </a-entity>

        <a-entity id="tree_blink-4" position="-0.111 3.017 -2.264" light="intensity:2;color:#ff0;groundColor:#fff;type:point">
            <a-animation attribute="light.intensity" from="0.5" to="1.5" dur="2000" delay="800" direction="alternate" repeat="indefinite"></a-animation>
        </a-entity>
</a-entity>

<!-- tree_ornaments -->
<a-entity id="tree_ornaments">
        <a-entity id="tree_ornament" position="-0.188 5.978 0.589" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#0ff"></a-entity>
        <a-entity id="tree_ornament-2" position="-1.046 4.315 0.717" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#ff0"></a-entity>
        <a-entity id="tree_ornament-3" position="0.248 4.287 0.36" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#0f0"></a-entity>
        <a-entity id="tree_ornament-4" position="0.653 2.871 0.431" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#0ff"></a-entity>
        <a-entity id="tree_ornament-5" position="-0.883 2.612 1.03" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#f00"></a-entity><a-entity id="tree_ornament-6" position="0.783 3.137 -1.083" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#0ff"></a-entity><a-entity id="tree_ornament-7" position="0.18 4.207 -1.262" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#0f0"></a-entity><a-entity id="tree_ornament-8" position="-0.288 5.45 -0.988" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#f00"></a-entity><a-entity id="tree_ornament-9" position="-0.248 6.548 -0.922" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#ff0"></a-entity><a-entity id="tree_ornament-10" position="-1.07 2.598 -0.81" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#ff0"></a-entity><a-entity id="tree_ornament-11" position="-0.772 2.299 0.13" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#f0f"></a-entity><a-entity id="tree_ornament-12" position="-1.451 5.014 -0.048" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#90f"></a-entity><a-entity id="tree_ornament-13" position="-0.408 5.014 0.335" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#fff"></a-entity><a-entity id="tree_ornament-14" position="0.451 5.298 -0.041" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#0f9"></a-entity><a-entity id="tree_ornament-15" position="0.451 4.006 -0.041" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#ff0"></a-entity><a-entity id="tree_ornament-16" position="0.337 5.199 -0.805" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#f0f"></a-entity><a-entity id="tree_ornament-17" position="-0.317 6.471 0.373" scale="0.1 0.1 0.1" geometry="primitive:sphere" material="color:#fff"></a-entity>
</a-entity>
</a-entity>
<!-- // end of tree_christmas -->

<a-entity id="snow"
geometry="primitive: sphere; radius: 0.4;"
material="shader: flat; color: #fff;"
position="0 -10 0"
></a-entity>

<a-entity id="building"
geometry="primitive: box; width: 4; height: 8; depth: 4;"
material="shader: flat"
position="0 -10 0"
>
</a-entity>

<a-entity id="camera_container"
position="0 8 20" rotation="10 0 0"
data-lookat="0 0 0"
>
    <a-camera id="camera_body"
    far="6000"
    look-controls-enabled="true"
    wasd-controls-enabled="false"
    ></a-camera>
</a-entity>
</a-entity>
</a-scene>
```

###### 1-3.3Dオブジェクトを操作するスクリプトを追加

カメラがツリーの周りをまわったり、ツリーへの照明が変化したり、雪が降ったりは、スクリプトで行います。

```html:(抜粋：スクリプト部分)
    <script>
    const settings = {
      building: 60,
      snow: 30
    };
    document.addEventListener('DOMContentLoaded', e => {
      setTimeout(app, 100);
    });
    const app = () => {
      const viewport = document.getElementById('viewport');

        <!-- ーー中略ーー -->

      destroy(delay){
        //super.destroy();
        this.status = 'will_destroy';
        this.acceleration.multiply(new THREE.Vector3(0.8, 0.25, 0.8));
        setTimeout(() => {
          this.setup();
        }, delay); 
      }
    }
    </script>
```

#### 2.アカウント情報を3Dオブジェクトにする

作成したindex.htmlは、[github](https://github.com/zgw426/Christmastree2019)の`index-r2.html`です。

ログインしたアカウントのアカウント情報を取得し3Dオブジェクトを作成します。これは、どちらもスクリプトで行いました。アカウント名は、ログインが成功した後に取得できるため、オブジェクト作成時はアカウント情報がわかりません。そのため、オブジェクト作成時は、アカウント名ではなく`dummy`という仮の文字列を設定します。

オブジェクトの上下移動処理に、アカウント情報が設定されたタグ（`<div id="usernameTag"`・・・）からアカウント名を取得し3Dオブジェクトを更新します。

```html:（抜粋：アカウント名の3Dオブジェクト作成と上下アニメーション）

// ユーザー名を設定する
<div id="usernameTag" class="user-name">tteesstt--</div>

<script>
// 3Dオブジェクト作成
var scene = document.querySelector('a-scene');
var cylinder = document.createElement('a-text');
cylinder.setAttribute('color', '#FF9500');
cylinder.setAttribute('value', "dummy");
cylinder.setAttribute('position', { x: 50, y: 20, z: 30 });
cylinder.setAttribute('width', '50');
scene.appendChild(cylinder);

// オブジェクトを上下に移動繰り返す
var t = 0;
function render() {
  t += 0.01;
  requestAnimationFrame(render);
  cylinder.setAttribute('position', '3 '+(Math.sin(t*2)+10)+' 0');
  var nameStr = "";
  nameStr += document.getElementById("usernameTag").innerText;
  nameStr += "\n Merry Christmas";
  cylinder.setAttribute('value', nameStr);
}
render();
</script>
```

## 課題

クリスマスツリーの描画処理はサイトを開いたタイミングで開始されます。さらに、ログイン処理が完了した後も再ロードされるので同じ処理を2回行っています。後半の1回だけに行うようにしたいところです。