Stosowanie zasad programowania miniaplikacji do przykładowego projektu

Domena aplikacji

Aby pokazać sposób programowania małej aplikacji w przypadku aplikacji internetowej, potrzebowałem pomysłu na małą, ale wystarczająco kompletną aplikację. Trening interwałowy o wysokiej intensywności (HIIT) to strategia ćwiczeń sercowo-naczyniowych polegająca na naprzemiennych zestawach krótkich, intensywnych ćwiczeń beztlenowych z mniej intensywnymi okresami regeneracji. Wiele treningów HIIT wykorzystuje minutniki HIIT, na przykład tę 30-minutową sesję online z kanału YouTube The Body Coach TV.

Sesja treningu HIIT online z zielonym timerem intensywności.
Okres aktywności.
Sesja online treningu HIIT z czerwonym licznikiem czasu o niskim stopniu intensywności.
Odpoczynek.

Przykładowa aplikacja HIIT Time

Na potrzeby tego rozdziału utworzyłem prosty przykład takiej aplikacji, która nosi trafną nazwę „HIIT Time”. Pozwala ona użytkownikowi definiować różne timery i nimi zarządzać. Każdy z nich składa się z interwału o wysokiej i niskiej intensywności. Następnie użytkownik może wybrać jeden z nich na potrzeby sesji treningowej. Jest to elastyczna aplikacja z paskiem nawigacyjnym i paskiem kart oraz 3 stronami:

  • Trening: aktywna strona podczas treningu. Umożliwia użytkownikowi wybranie jednego z zegarów. Zawiera 3 okręgi postępu: liczbę serii, okres aktywny i okres odpoczynku.
  • Timery: umożliwia zarządzanie istniejącymi zegarami i tworzenie nowych.
  • Ustawienia: umożliwiają przełączanie efektów dźwiękowych i wyświetlania mowy oraz wybór języka i motywu.

Poniżej znajdziesz zrzuty ekranu przedstawiające aplikację.

Przykładowa aplikacja HIIT Time w trybie pionowym
Karta „Trening” czasu HIIT w trybie portretowym.
Przykład aplikacji HIIT Time w orientacji poziomej
HIIT Time „Workout” (Trening) w układzie poziomym.
Przykładowa aplikacja HIIT Time pokazująca zarządzanie timerem.
Ustawienia minutnika HIIT Time.

Struktura aplikacji

Jak wspomnieliśmy powyżej, aplikacja składa się z paska nawigacyjnego, paska kart i 3 stron rozmieszczonych w siatce. Pasek nawigacyjny i pasek kart są realizowane jako elementy iframe z kontenerem <div>, pomiędzy którymi znajdują się 3 kolejne elementy iframe dla stron. Jeden z nich jest zawsze widoczny i zależy od aktywnego wyboru na pasku kart. Końcowy element iframe wskazujący about:blank jest wyświetlany w przypadku dynamicznie tworzonych stron w aplikacji, które są potrzebne do modyfikowania istniejących liczników czasu lub tworzenia nowych. Nazywamy ten wzorzec wielostronicową aplikacją jednostronicową (MPSPA).

Widok struktury HTML aplikacji w Narzędziach deweloperskich Chrome, który pokazuje, że składa się ona z 6 ramek iframe: jednej dla paska nawigacyjnego, jednej dla paska kart i 3 ramek zgrupowanych dla każdej strony aplikacji oraz ostatniej ramki iframe jako placeholdera dla stron dynamicznych.
Ta aplikacja składa się z 6 elementów iframe.

znaczniki lit-html oparte na komponentach;

Struktura każdej strony jest realizowana jako szkielet lit-html, który jest dynamicznie oceniany w czasie wykonywania. Lit-html to wydajna, elastyczna i rozszerzalna biblioteka szablonów HTML do JavaScriptu. Dzięki temu, że model jest używany bezpośrednio w plikach HTML, jest on bezpośrednio ukierunkowany na dane wyjściowe. Jako programista piszesz szablon przedstawiający ostateczne dane wyjściowe, a następnie lit-html dynamicznie wypełnia luki na podstawie Twoich danych i łączy detektory zdarzeń. Aplikacja korzysta z elementów niestandardowych innych firm, takich jak <sl-progress-ring> firmy Shoelace lub z elementu niestandardowego o nazwie <human-duration>, który został zaimplementowany przez użytkownika. Elementy niestandardowe mają deklaratywny interfejs API (np. atrybut percentage pierścienia postępu), więc dobrze współdziałają z lit-html, jak widać na poniższej liście.

<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 przyciski i okrąg postępu.
Renderowana sekcja strony odpowiadająca znacznikom powyżej.

Model programowania

Każda strona ma odpowiadającą jej klasę Page, która wypełnia znaczniki znaczników lit-html, zapewniając implementacje obsługi zdarzeń i dane dla każdej strony. Ta klasa obsługuje też metody cyklu życia, takie jak onShow(), onHide(), onLoad()onUnload(). Strony mają dostęp do magazynu danych, który służy do udostępniania opcjonalnie utrwalonego stanu na stronie i stanu globalnego. Wszystkie ciągi tekstowe są zarządzane centralnie, więc międzynarodowość jest wbudowana. Przekierowanie jest obsługiwane przez przeglądarkę praktycznie bezpłatnie, ponieważ aplikacja tylko przełącza widoczność iframe i w przypadku stron tworzonych dynamicznie zmienia atrybut src iframe zastępczego. Przykład poniżej pokazuje kod służący do zamykania strony utworzonej dynamicznie.

import Page from '../page.js';

const page = new Page({
  eventHandlers: {
    back: (e) => {
      e.preventDefault();
      window.top.history.back();
    },
  },
});
Strona w aplikacji zrealizowana jako element iframe.
Nawigacja odbywa się z jednego elementu iframe do drugiego.

Styl

Stylowanie stron odbywa się w ramach ich własnych plików CSS. Oznacza to, że elementy można zwykle adresować bezpośrednio, podając ich nazwy, ponieważ nie dochodzi do kolizji z innymi stronami. Style globalne są dodawane do każdej strony, więc centralne ustawienia, takie jak font-family czy box-sizing, nie muszą być deklarowane wielokrotnie. Tutaj możesz też określić motywy i opcje trybu ciemnego. Poniższa lista zawiera reguły dotyczące strony Ustawienia, na której znajdują się różne elementy formularza w siatce.

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;
}
Strona ustawień aplikacji HIIT Time przedstawiająca formularz w układzie siatki.
Każda strona jest innym światem. Styl wprowadza się bezpośrednio z nazwami elementów.

Blokada wybudzania ekranu

Podczas treningu ekran nie powinien się wyłączać. W przeglądarkach, które to umożliwiają, HIIT Time realizuje to za pomocą blokady ekranu. Poniżej znajdziesz fragment kodu, który to pokazuje.

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();
    }
  });
}

Testowanie aplikacji

Aplikacja HIIT Time jest dostępna na GitHubzie. Możesz wypróbować demo w nowym oknie lub w ramce iframe umieszczonej poniżej, która symuluje urządzenie mobilne.

Podziękowania

Ten artykuł został sprawdzony przez Joe Medley, Kayce Basques, Milica Mihajlija, Alan Kent i Keitha Gu.