PDCAを物理的に回すサービスを Vue.js + typescript で作ってみた（RTA）

この記事は [ドワンゴ Advent calender 2019](https://qiita.com/advent-calendar/2019/dwango) の7日目です。（日にち変わってしまった...）

おばんです。ドワンゴジェイピーという音楽配信サイトのバックエンドを保守している者です。
今回は仕事のネタではなく、趣味で作ろうかなと思っていたサービスを、この機会を借りて作ってみた話をします。
作り始めるのがギリギリになってしまったので、RTAだと開き直ります！

# 何を作ったの

弊社の Slack には「経済が回る絵文字」があります。社内オークションチャンネルや、お金の話題をするチャンネルで頻用されます。
シュールさが大好きで HTML で再現してみたいなあと常日頃思っていました。

私は「PDCAが回る」ものを作りました。せっかく PDCA なので、入力できる機能もつけようかなと。
ついでに TypeScript の肩慣らし。

動くものはこちらです: https://irimo.github.io/rolling-pdca/dist/

- 右横の PDCA がスクロールすると動きます。
- PDCA を入力すると、localStorage に保存されて、自分だけ見返せます。

本当はリアル「PDCAを回す」ために、クリックでコンポーネントを増殖させる機能もつけたかったのですが、またの機会に。

# HTML でイメージをつかんだ

PDCA がスクロールで回るものを、まずは JavaScript で作ってみました。

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <script>
        window.onscroll = function()
{
    var scrollTop =
        document.documentElement.scrollTop || // IE、Firefox、Opera
        document.body.scrollTop;              // Chrome、Safari
    var absdiv = document.getElementById("absdiv");
    absdiv.style = "transform: rotate("+scrollTop+"deg)";
}
    </script>
    <style type="text/css">
    #absdiv {
      display: inline-block;
      font-size: 3em;
      position:fixed;
      bottom:20px;
      right:20px;
    }
     
</style>
</head>
<body>
    <div id="absdiv">
    <div class="pdca-wrap">PDCA<br> </div>
</div>
</body>
</html>
```

素の JS は、懐かしい感じがしますね。

# いよいよ Vue + TypeScript を導入

本当は vue コマンドで作った方がいいのですが、今回は RTA なので、先人の肩に乗っかりまくって、ネットに転がっているテンプレから作りました。（ありがとうございます:pray:）[^1]

Vue も TypeScript も初ですが、 React を 0.X の時に少し触ったので、コンポーネントの作り方は知っています。そういう方は理解も早いと思います。

[^1]: https://ics.media/entry/16329/#webpack-ts-vue

# PDCA を回すコンポーネント

こちらできたものです。

```ts

@Component({
  template: `
  <div class="pdca-wrap" v-bind:style="{transform: styleObject}">PDCA<br> </div>
`,
})

export default class PdcaComponent extends Vue {
  scrollTop: number = 0;
  styleObject: String = "rotate(0deg)"
  mounted(): void {
    window.addEventListener('scroll', this.scrollSpy);
  }
  destroyed(): void {
    window.removeEventListener('scroll', this.scrollSpy);
  }
  scrollSpy() {
      this.scrollTop =
          document.documentElement.scrollTop || // IE、Firefox、Opera
          document.body.scrollTop;              // Chrome、Safari
      this.styleObject = "rotate(" + this.scrollTop + "deg)";
    }
  }
```
本当は、しかるべきタイミングで PDCA が激しく回るようなパラメタを作りたかったのですが、まだその挙動はイメージが沸かないので保留。

scroll の取り方が、 JavaScript よりスマートですね。
window が取れなかったらググってください[^2]。

`transform: rotate(scrollTop + deg)`という文字列を div の style に渡すとスクロールに合わせてくるくる回るのですが、指定の方法が他の CSS 記法と違うので、手こずりました。

[^2]: https://sansaisoba.qrunch.io/entries/NrmnZGUHE3xZhO3a

# PDCA を入力するコンポーネント

できたものはこちらです。

```ts

@Component({
  template: `
  <div class="inputform">
  <form id="myform">
  <span>Plan: </span><input type="text" id="form-plan" v-model:value="t_plan" placeholder="計画を書こう" /><br />
  <span>Do: </span><input type="text" id="form-do" v-model:value="t_do" placeholder="やることに落としこもう" /><br />
  <span>Check: </span><input type="text" id="form-check" v-model:value="t_check" placeholder="やったこと確認しよう" /><br />
  <span>Action: </span><input type="text" id="form-action" v-model:value="t_action" placeholder="次になにする" /><br />
  </form>
  </div>
`,
})

export default class InputForm extends Vue {
        t_plan: string =  "";
        t_do: string =  "";
        t_check: string =  "";
        t_action: string =  "";
constructor() {
        super();
        var plan: string = localStorage.getItem('plan1');
        this.t_plan = plan ? plan : "";
        var val_do: string = localStorage.getItem('do1');
        this.t_do = val_do ? val_do : "";
        var check: string = localStorage.getItem('check1');
        this.t_check = check ? check : "";
        var action: string = localStorage.getItem('action1');
        this.t_action = action ? action : "";
    }
  mounted(): void {
    this.$el.querySelector("#form-plan").addEventListener('keyup', this.blurSpy);
    this.$el.querySelector("#form-do").addEventListener('keyup', this.blurSpy);
    this.$el.querySelector("#form-check").addEventListener('keyup', this.blurSpy);
    this.$el.querySelector("#form-action").addEventListener('keyup', this.blurSpy);
  }
  destroyed(): void {
  }

      blurSpy() {
        localStorage.setItem('plan1', this.t_plan);
        localStorage.setItem('do1', this.t_do);
        localStorage.setItem('check1', this.t_check);
        localStorage.setItem('action1', this.t_action);
    }
  }
```

本当は配列にした方が利便性がよさそうですが、RTA なので...

ググってもわからなかったのですが、input の値を取ったり差し込んだりしたいときは、`v-model:value`とするとよいです。

作成時は仕上げに EventListener を `keyup` にしたので `blur` だったものがメソッド名に残っていますが、blur だと入力中にリロードした際、入力内容が消えてしまうのでよくないです。
keyup のレイテンシが気になったのですが、今時大丈夫でした。

localStorage はいきなり `localStorage` で見られると知ってちょっとびびりました。なんか変なストリーム読み込んだりして頑張るイメージがあったのですが、良かったです。いろいろ気軽に使いたいところ。

# まとめ

- 素の JS より分かりやすく作れた
- コンポーネントへ style の適用方法、form へ localStorage のデータを差し込む方法がわかった
- 何にせよ実質5時間ちょっとで作れちゃったのは時代の進化か、ネットの恩恵、デバッグも楽だった

# おわりに

今回のメインは PDCA が物理的に回るコンポーネントなので何なんですが、PDCA という振り返りは古いという話を聞きました。振り返りのベストプラクティスとは。どのみち localStorage は使えると思うので、いろんな振り返りサービスを見てみたいものです。