フロントエンドフレームワークを使うと、dom の管理を自動化することができると記事を以前書かせてもらいました。
今回はフレームワークの中でも分かりやすい Vue.js を使って新春おみくじアプリを作りました
モダンみくじ
完成品はこちらです。画像では分かりませんがおみくじの箱がゆれています。
https://manybugsdev.github.io/mikuji/

クリックすると大凶とか出てきます。

結果詳細画面が出てきて、ボタンをクリックするともう一回引けます。

Vue のインストール
Vue はビルドステップを挟まずに使用することができます。(nodejs とかいれなくても使い始められるということです)
使い始めるには cdn から script タグで読み込みます。もしくは最近では、script タグに type="module"を設定することで esm 構文で書くのがイけてますのでそちらでやってみましょう。
data, methods
おみくじに使うデータを作成します。
<script type="module">
import { createApp } from "https://unpkg.com/vue@3/dist/vue.esm-browser.js";
createApp({
data() {
return {
result: null, // 引いたおみくじ。nullは何も引いていない状態
};
},
methods: {
draw() {
// おみくじを引く
const index = Math.floor(Math.random() * sticks.length);
this.result = sticks[index];
},
back() {
// おみくじを戻す
this.result = null;
},
},
}).mount("#app");
</script>
これで id="app"の dom に対して vue app がバインドされます。data()という部分がアプリのデータを表しており、methods は data に対する操作を表しています。
result はおみくじの結果データを保持する変数です。最初はおみくじを引いていないので null になっています。sticks という配列におみくじのデータが保存されており、name と summary というプロパティを持ちます。まあ、見てもらった方が早いと思います。
const sticks = [
{
name: "大吉",
summary: "何をやってもうまくいく日。全てのことが順調に運ぶでしょう。",
},
{
name: "吉",
summary: "良い日。何をやってもうまくいくでしょう。",
},
...
draw メソッドでは、 sticks の中から 1 つデータをランダムに選択し result に保存します。おみくじを引くという動作ですね。back では逆におみくじを捨てるため result を null にします。
このようにデータの流れだけみるとおみくじは非常に単純な構造になっています。このデータの状態に応じてテキスト内容やアニメーションや色などを設定していきます。
template
vue ではこのような template を作っていくことでデータと見た目を結びつけていきます。
<text x="20" y="-2" rotate="-90" transform="rotate(90)" font-weight="bold">
{{result?.name}}
</text>
text ノードのテキストに result.name をバインディングしています。draw を呼び出して result を変更するとテキストの内容が自動で変わってくれます。
<svg
@click="draw"
class="mikuji"
:class="{completed: result}"
width="200px"
height="400px"
viewBox="0,0,200,400"
></svg>
@click ではメソッドのバインディングを行っており、この要素がクリックされた時に draw メソッドが呼ばれるようにしています。addEventListener とかをいちいち呼ばなくていい訳です。また、dom の属性に対してもデータバインディングを行うことができ、:class では result が null でなくなったときにこの要素に completed というクラスを追加するようにしています。追加した際の見た目を css で変更することでアニメーションなどを設定しています。
.mikuji {
user-select: none;
cursor: pointer;
animation: shake 1s infinite;
}
.mikuji.completed {
animation: none;
}
dom 操作はしなくてよくなりますが、html や css で望み通りの見た目を作るのがとてもしんどいので結局大変でした。おわり。