アプリのドメイン
ウェブアプリに適用されるミニアプリによるプログラミングを示すには、小さいながらも十分なアプリのアイデアが必要でした。高強度インターバル トレーニング(HIIT)は、短時間の激しい嫌気性運動を交互に行う心臓血管の運動戦略で、回復時間はそれほど大きくありません。多くの HIIT トレーニングでは HIIT タイマーを使用します。たとえば、YouTube チャンネルの The Body Coach TV の 30 分間のオンライン セッションなどです。
HIIT Time サンプルアプリ
この章では、このような HIIT タイマー アプリケーションの基本的な例を作成しました。このアプリケーションの名前は「HIIT Time」です。ユーザーは、さまざまなタイマーを定義して管理できます。これらのタイマーは常に高強度と低強度のインターバルで構成され、トレーニング セッションに 1 つを選択できます。ナビゲーション バー、タブバー、3 つのページを備えたレスポンシブ アプリです。
- ワークアウト: ワークアウト中のアクティブなページ。ユーザーはタイマーの 1 つを選択し、セット数、アクティブ期間、休憩期間の 3 つの進行状況リングを表示できます。
- タイマー: 既存のタイマーを管理し、ユーザーが新しいタイマーを作成できるようにします。
- 設定: 効果音と音声出力の切り替え、言語とテーマの選択ができます。
次のスクリーンショットは、アプリケーションの概要を示しています。
アプリの構造
上記のように、このアプリはナビゲーション バー、タブバー、3 つのページで構成され、グリッド状に配置されています。ナビゲーションバーとタブバーは iframe として実現され、その間に <div>
コンテナがあり、ページ用にさらに 3 つの iframe があります。そのうちの 1 つは常に表示され、タブバーでのアクティブな選択に依存します。about:blank
を指す最後の iframe は、動的に作成されたアプリ内ページに使用されます。これは、既存のタイマーの変更や新しいタイマーの作成に必要です。私はこのパターンをマルチページ シングルページ アプリ(MPSPA)と呼んでいます。
コンポーネントベースの lit-html マークアップ
各ページの構造は、実行時に動的に評価される lit-html スキャフォールドとして実現されます。lit-html の背景には、効率的で表現力が高く、拡張可能な JavaScript 用の HTML テンプレート ライブラリです。HTML ファイルで直接使用することで、メンタル プログラミング モデルは直接出力指向になります。プログラマーは、最終出力がどのようなものになるかのテンプレートを作成します。その後、lit-html がデータに基づいてそのギャップを動的に埋め、イベント リスナーを接続します。このアプリは、Shoelace の <sl-progress-ring>
などのサードパーティ製カスタム要素や、<human-duration>
という独自実装のカスタム要素を使用しています。カスタム要素には宣言型 API(進行状況リングの percentage
属性など)があるため、下記のリストに示すように、lit-html とうまく連携します。
<div>
<button class="start" @click="${eventHandlers.start}" type="button">
${strings.START}
</button>
<button class="pause" @click="${eventHandlers.pause}" type="button">
${strings.PAUSE}
</button>
<button class="reset" @click="${eventHandlers.reset}" type="button">
${strings.RESET}
</button>
</div>
<div class="progress-rings">
<sl-progress-ring
class="sets"
percentage="${Math.floor(data.sets/data.activeTimer.sets*100)}"
>
<div class="progress-ring-caption">
<span>${strings.SETS}</span>
<span>${data.sets}</span>
</div>
</sl-progress-ring>
</div>
プログラミング モデル
各ページには、イベント ハンドラの実装と各ページのデータを提供することで、lit-html マークアップに生命を吹き込む対応する Page
クラスがあります。このクラスは、onShow()
、onHide()
、onLoad()
、onUnload()
などのライフサイクル メソッドもサポートしています。ページは、必要に応じて永続化されたページごとの状態とグローバル状態を共有するデータストアにアクセスできます。すべての文字列が集中管理されるため、国際化が組み込まれています。アプリは iframe の表示の切り替えだけを行い、動的に作成されたページではプレースホルダ iframe の src
属性を変更するため、ルーティングは基本的にブラウザによって処理されます。以下の例は、動的に作成されたページを閉じるコードを示しています。
import Page from '../page.js';
const page = new Page({
eventHandlers: {
back: (e) => {
e.preventDefault();
window.top.history.back();
},
},
});
スタイル設定
ページのスタイル設定は、独自のスコープ CSS ファイルでページごとに行われます。つまり、他のページと競合することがないため、通常は要素名で要素を直接指定できます。グローバル スタイルは各ページに追加されるため、font-family
や box-sizing
などの一元的な設定を繰り返し宣言する必要はありません。ここでは、テーマとダークモードのオプションも定義されます。
以下のリストは、さまざまなフォーム要素をグリッドに配置する [設定] ページのルールを示しています。
main {
max-width: 600px;
}
form {
display: grid;
grid-template-columns: auto 1fr;
grid-gap: 0.5rem;
margin-block-end: 1rem;
}
label {
text-align: end;
grid-column: 1 / 2;
}
input,
select {
grid-column: 2 / 3;
}
画面の wake lock
ワークアウト中は画面がオフにならないようにする必要があります。これをサポートしているブラウザでは、HIIT Time は画面ウェイクロックを使用してこれを実現します。以下のスニペットは、その方法を示しています。
if ('wakeLock' in navigator) {
const requestWakeLock = async () => {
try {
page.shared.wakeLock = await navigator.wakeLock.request('screen');
page.shared.wakeLock.addEventListener('release', () => {
// Nothing.
});
} catch (err) {
console.error(`${err.name}, ${err.message}`);
}
};
// Request a screen wake lock…
await requestWakeLock();
// …and re-request it when the page becomes visible.
document.addEventListener('visibilitychange', async () => {
if (
page.shared.wakeLock !== null &&
document.visibilityState === 'visible'
) {
await requestWakeLock();
}
});
}
アプリケーションのテスト
HIIT Time アプリケーションは GitHub で入手できます。デモは、新しいウィンドウで再生できます。また、モバイル デバイスをシミュレートする以下の iframe 埋め込みで直接再生することもできます。
謝辞
この記事は、Joe Medley、Kayce Basques、Milica Mihajlija、Alan Kent、Keith Gu が確認しました。