アイマス会話メーカーで使っている技術・テクニックまとめ

※アイマス会話メーカーの説明→[おはなし・デレぽ作成ツール『アイマス会話メーカー』の紹介と使い方](http://ysrken.seesaa.net/article/ohanashigen.html)
※GitHub→[https://github.com/YSRKEN/ohanashi](https://github.com/YSRKEN/ohanashi)
※Ver.1.XのURL→[https://ohanashigen.firebaseapp.com](https://ohanashigen.firebaseapp.com)
※Ver.2.X(開発中)のURL→[https://imas-talk-maker.firebaseapp.com](https://imas-talk-maker.firebaseapp.com)

# ライブラリ・フレームワーク編

## [Angular](https://angular.jp)

　Google製フルスタックWebフロントエンドフレームワーク(プロジェクトではAngular 6を採用)。
　コンポーネントがclassになっていて、ReactiveExtentionで繋ぎ合わせるデザイン設計といった印象。
　コンポーネント作成・CSS設定・ルーティング・DI・通信など様々なライブラリがひとまとめになっているため、Angular-CLIさえ入れれば細かいことを考えなくてもいいのが良さ。
　ただ、コンポーネントを作成して@Input・@Outputを割り振って組み込む……といった処理が(Reactと比べて)面倒臭く、結局「おはなし」「デレぽ」のレンダリング部分が雑になってしまっていた。つまり、「外部からのフラグを@Inputから入力すると、CSSによってHTMLのタグブロックが表示/非表示になる」などといった力技である。
　また、**秒でコンポーネントを作成できる**Reactと比べると、コマンドを打ってガッチリしたコンポーネント(の雛形)を作成するAngularの書き味は重いといった難点もある。このことから、Ver.2.X以降はReactで書く予定。

## [React](https://reactjs.org)

　Facebook製Webフロントエンドライブラリ。
　仮想DOM・コンポーネント志向・JSX記法を引っさげて登場し、瞬く間に大人気になったとされている。
　オブジェクト指向のお手本のようなAngularと比べて、関数型プログラミングで書くために生まれたかのような設計思想。
　それでいて、基本的にMVCで言うところのView用のライブラリなので、MとVをどうするかがしばしば問題になる。
　表示しかできないStateless Functional ComponentにHOCで状態を付与するのが基本テクニックだった。しかし、近頃はReact Hooksにより、「状態」や「初期化/更新/破棄時の動作」や「DI」といった操作をより簡単に行えるようになった。
　また、状態管理のテクニックとしてFlux(Redux)がよく知られているが、「規模が小さい」「理念は分かるがコーディング作法がよくわからない」といった理由により、Ver.2.Xでは採用していない。

## [Bootstrap](https://getbootstrap.com)

　Twitter製CSSフレームワーク(プロジェクトではBootstrap 4を採用)。
　手軽に整ったレイアウトを作成できる他、グリッドシステムにより凝ったレイアウトも作りやすいのが特徴。
　ただしJavaScriptで動きを制御する(省くと一部機能が使えなくなる)ことと、ライブラリ時代のサイズがそこそこ大きいことから、「あえてBootstrapじゃないCSSフレームワークにしようぜ！」といった動きもある。
　当プロジェクトでも採用しているが、Ver.2.XではCSSのみ採用し、JSが必要な部分は自前で書くことによって対処予定。

## [html2canvas](https://html2canvas.hertzen.com)

　HTML/CSSで描かれたDOMをcanvasとして描画し直してくれるライブラリ。
　これにより、構築したおはなし・デレぽのプレビューがcanvas化されるため、そのまま**画像として保存可能**になる。
　ただ、ライブラリがHTML/CSSを完璧に解釈してくれるわけではないため、レイアウト崩れが起きてしまうこともあるのが難点。

## [TypeScript](https://www.typescriptlang.org)

　**トランスコンパイルしなくてもブラウザで動くようになってくんねーかな。**
　型安全にできるので当プロジェクトでも当然採用。

## [ng-lazyload-image](https://www.npmjs.com/package/ng-lazyload-image)

　画像を遅延評価できる、Angular用のライブラリ。Ver.1.Xでは、キャラ選択機能と表情選択機能が分離されていないということもあり、通信量を節約するために採用していた。ただ、[スクロールなどのイベントを拾わないと読み込みが始まらなかったりするクセ](https://github.com/tjoskar/ng-lazyload-image/issues/197)があるため、読み込み量を工夫して削減したVer.2.Xでは遅延読み込みを不採用予定。

# 小技編

## おはなし/デレぽのレンダリング方法

　当初はCSSだけで全部レンダリングしていましたが、

<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="ja" dir="ltr">【お知らせ】<br>自分用おはなしジェネレーターを公開しました。特徴としては、<br>・プリセット部分を入れ替えるだけで任意の作品に対応できる<br>・そもそもURLを直打ちすれば画像差し替えは容易<br>・キャラ名部分も自由に設定可能<br>・Googleてんてーによる驚異のCDN<br>・PWA<a href="https://t.co/jvVXzaCOcF">https://t.co/jvVXzaCOcF</a> <a href="https://t.co/nSBkoisvAV">pic.twitter.com/nSBkoisvAV</a></p>&mdash; Chiyuki Kuwayama (@YSRKEN) <a href="https://twitter.com/YSRKEN/status/1018322210551992321?ref_src=twsrc%5Etfw">July 15, 2018</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

後に[おはなしジェネレーター](https://github.com/furugomu/ohanashi/tree/master/images)から背景素材を拝借して利用する形にしました。そうしないとhtml2canvasでのレンダリング結果の再現度が低くなるもの……orz

　また、「おはなし」および「デレぽ」部分は、大枠を「`position:relative;`」と指定し、その中の要素を「`position:absolute;`」とすることで組んでいます。つまり、大枠以外の要素は「大枠の左上座標」を基準にした相対座標で配置するわけですね。

　[CSSのposition: absoluteとrelativeとは | UX MILK](https://uxmilk.jp/63409)

　Ver.1.Xでは「おはなし」と「デレぽ」をpx指定で描いていました。しかし、レスポンシブ対応を考えると好ましいものではありません。そこでVer.2.Xでは、大枠以外を大枠に対する％指定で表現し、大枠に対してwidth/heightを独自に設定することで対処しました。ただ、

- どうやって縦横比を保った状態でリサイズするのか
- フォントサイズはどのように調整するのか

が課題でした。前者は[CSSハック](https://qiita.com/ryounagaoka/items/a98f59347ed758743b8d)できそうでしたが可読性が悪化しそうであり、後者は**fontサイズがwidthやheightとはまったく別の基準で決められているCSSのクソ仕様**によりJavaScriptでの対策が急務でした。

　[[CSS]フォントサイズの指定方法の基礎知識からレスポンシブに適した指定方法までやさしく解説](https://coliss.com/articles/build-websites/operation/css/css-font-sizing-by-ire.html)

　そこで考えたのが、

- 「おはなし」や「デレぽ」のコンポーネントの読み込み時に、[HTMLElement.offsetWidth](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetWidth)を読み取る(Xと置く)
- コンポーネントの横幅および縦幅をX基準で決める。例えば「`width=Xpx;height=calc(X*2/3)px;`」など
- フォントサイズもX基準にする。例えば「`const Y = X / 10; font-size: Ypx;`」など

といった戦法でした。別作品「[シャニマス会話メーカー](https://shiny-maker.firebaseapp.com)」ではこれを[Angularで実装し](https://github.com/YSRKEN/shiny_maker/blob/master/ShinyMaker/src/app/message-window/message-window.component.ts)、アイマス会話メーカーVer.2.Xでは[Reactで実装しています](https://github.com/YSRKEN/ohanashi/blob/feature/create-2nd-edition/imas-talk-maker/src/component/OhanashiView.tsx)。

　ちなみに、おはなしは「ファボるボタンとメッセージ欄の高さによって時刻欄の位置が変わる」といった仕様があることが発覚したので、これからそれを実装するところですorz

<blockquote class="twitter-tweet" data-partner="tweetdeck"><p lang="ja" dir="ltr">このことに気づいているデレステPはほぼいないと思いますが、<br>「デレぽ」における投稿時刻表示の縦位置は、<br>「右のファボボタン」からの相対位置ではなく、<br>「右のファボボタン・メッセージ欄の縦に長い方」<br>からの相対位置で決まっています。<br>なお画像・ハッシュタグは「メッセージ欄」に含みます。 <a href="https://t.co/kNbpM9QqSM">pic.twitter.com/kNbpM9QqSM</a></p>&mdash; Chiyuki Kuwayama (@YSRKEN) <a href="https://twitter.com/YSRKEN/status/1102089005900816384?ref_src=twsrc%5Etfw">March 3, 2019</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## ツイートボタンの作成方法

　[Angularのコンポーネントにツイートボタンを載せる - Qiita](https://qiita.com/agajo/items/863dbaa1447508289b07)を参考にしました。Reactの場合はどうするかって？　**検討中です☆**

## 水平切り替えボタン

　Ver.2.Xより実装。[Bootstrapのボタングループ機能](https://cccabinet.jpn.org/bootstrap4/components/button-group)を参考に、[「選択されている」ボタンのCSSのみ書き換えるような都合のいいコンポーネントをReactで作成](https://github.com/YSRKEN/ohanashi/blob/feature/create-2nd-edition/imas-talk-maker/src/component/SelectButtonGroup.tsx)することで対処しました。

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">私「Reactで選択肢ボタンを作成するコンポーネントを作成しました」<br>P「なんで上から順に不穏度が増していくの！？」 <a href="https://t.co/M8r7aQZK8H">pic.twitter.com/M8r7aQZK8H</a></p>&mdash; Chiyuki Kuwayama (@YSRKEN) <a href="https://twitter.com/YSRKEN/status/1100389573492797440?ref_src=twsrc%5Etfw">2019年2月26日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 横幅によって「アイマス会話メーカー」の文字の大きさを変える方法

　もちろんメディアクエリ……と言いたいところですが、Bootstrapの場合、[「横幅によって対象の表示可否を切り替える」ためのCSS](https://cccabinet.jpn.org/bootstrap4/utilities/display)がありますのでそれを利用ししました。