ミニアプリのプログラミングの原則をサンプル プロジェクトに適用する

アプリのドメイン

ウェブアプリに適用されるミニアプリのプログラミング方法 を示すには、小規模ながら十分なアプリのアイデアが必要でした。 高強度インターバル トレーニング(HIIT) は、高強度の無酸素運動を短時間行うセットと、低強度の回復期間を交互に行う心血管運動戦略です。多くの HIIT トレーニングでは、HIIT タイマーを使用します。たとえば、この 30 分間のオンライン セッション The Body Coach TV YouTube チャンネルなどです。

緑色の高強度タイマーを使用した HIIT トレーニングのオンライン セッション。
アクティブ期間。
赤い低強度のタイマーが表示された HIIT トレーニングのオンライン セッション。
休息期間。

HIIT Time サンプルアプリ

この章では、ユーザーがさまざまなタイマーを定義して管理できる「HIIT Time」という HIIT タイマーアプリの基本的な例を作成しました。タイマーは常に高強度と低強度のインターバルで構成され、トレーニング セッション用にいずれかを選択できます。 これは、ナビゲーション バー、タブバー、3 つのページを備えたレスポンシブ アプリです。

  • ワークアウト: ワークアウト中のアクティブなページ。ユーザーはタイマーのいずれかを選択でき、セット数、アクティブ期間、休息期間の 3 つのプログレス リングが表示されます。
  • タイマー: 既存のタイマーを管理し、新しいタイマーを作成できます。
  • 設定: 効果音と音声出力を切り替えたり、言語とテーマを選択したりできます。

次のスクリーンショットは、アプリケーションのイメージを示しています。

縦向きの HIIT Time サンプルアプリ。
縦向きモードの HIIT Time [ワークアウト] タブ。
横表示の HIIT Time サンプルアプリ。
横表示の HIIT Time [ワークアウト] タブ。
タイマーの管理を示す HIIT Time のサンプルアプリ。
HIIT Time タイマー管理。

アプリの構造

前述のように、アプリはナビゲーション バー、タブバー、3 つのページで構成され、グリッドに配置されています。 ナビゲーション バーとタブバーは iframe として実現され、その間に <div> コンテナがあり、ページ用の iframe が 3 つあります。このうち 1 つは常に表示され、タブバーのアクティブな選択に依存します。about:blank を指す最後の iframe は、動的に作成されたアプリ内ページに使用されます。これは、既存のタイマーを変更したり、新しいタイマーを作成したりするために必要です。このパターンをマルチページ シングルページ アプリ(MPSPA)と呼びます。

アプリの HTML 構造の Chrome DevTools ビュー。アプリが 6 つの iframe で構成されていることが示されています。1 つはナビバー用、1 つはタブバー用、3 つはアプリの各ページ用でグループ化され、最後のプレースホルダ iframe は動的ページ用です。
アプリは 6 つの iframe で構成されています。

コンポーネント ベースの lit-html マークアップ

各ページの構造は、lit-html スキャフォールド として実現されます。 実行時に動的に評価されます。lit-html は、JavaScript 用の効率的で表現力豊かで拡張可能な HTML テンプレート ライブラリです。 HTML ファイルで直接使用することで、メンタル プログラミング モデルは直接出力指向になります。 プログラマーは最終的な出力のテンプレートを作成し、lit- html はデータに基づいてギャップを動的に埋め、イベント リスナーをフックします。 このアプリでは、Shoelace's <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>
3 つのボタンと進捗状況リング。
上記のマークアップに対応するページのレンダリングされたセクション。

プログラミング モデル

各ページには、イベント ハンドラの実装を提供し、各ページのデータを提供することで、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();
    },
  },
});
アプリ内ページが iframe として実現されています。
ナビゲーションは iframe 間で行われます。

スタイル設定

ページのスタイル設定は、独自のスコープ付き CSS ファイルでページごとに行われます。 つまり、他のページとの競合が発生しないため、通常は要素名を直接指定できます。 グローバル スタイルは各ページに追加されるため、font-familybox-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;
}
グリッド レイアウトのフォームが表示されている HIIT Time アプリの設定ページ。
各ページは独自の環境です。スタイル設定は要素名で直接行われます。

画面の wake lock

ワークアウト中は、画面がオフにならないようにする必要があります。 HIIT Time は、画面の wake lockをサポートするブラウザでこれを実現します。次のスニペットにその方法を示します。

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 MedleyKayce BasquesMilica MihajlijaAlan Kent、 Keith Gu によってレビューされました。