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ę. Intensywny trening interwałowy (HIIT) to strategia ćwiczeń kardio polegająca na naprzemiennym wykonywaniu krótkich okresów intensywnego wysiłku beztlenowego i mniej intensywnych okresów odpoczynku. 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.
Aktywny okres.
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”. Umożliwia ona użytkownikowi definiowanie różnych liczników czasu i zarządzanie nimi (zawsze obejmują one interwał o wysokiej i niskiej intensywności), a potem wybranie jednego z nich na potrzeby sesji treningowej. Jest to responsywna aplikacja z paskiem nawigacji, paskiem kart i 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 tekstu oraz wybór języka i motywu.

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

Przykładowa aplikacja HIIT Time w trybie pionowym
HIIT Time, karta „Workout” (Trening) w orientacji pionowej.
Przykładowa aplikacja HIIT Time w trybie poziomym
HIIT Time „Workout” (Trening) w układzie poziomym.
Przykładowa aplikacja HIIT Time pokazująca zarządzanie timerem.
HIIT Zarządzanie czasem.

Struktura aplikacji

Jak już wspomnieliśmy, aplikacja składa się z paska nawigacyjnego, paska kart i 3 stron ułożonych w siatkową siatkę. 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 elementu na pasku kart. Ostatni tag iframe wskazujący na about:blank służy do dynamicznego tworzenia stron w aplikacji, które są potrzebne do modyfikowania dotychczasowych zegarów lub tworzenia nowych. Nazywam ten wzór wielostronicową aplikacją jednostronicową (MPSPA).

Widok struktury HTML aplikacji w narzędziach programistycznych Chrome, który pokazuje, że składa się ona z 6 ramek iframe: jednej dla paska nawigacyjnego, jednej dla paska kart i 3 ramek dla każdej strony aplikacji, a także ramki iframe jako elementu zastępczego 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, który będzie wyglądać jak wynik końcowy, a lit-html wypełnia luki dynamicznie na podstawie Twoich danych i podłącza 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. Ponieważ elementy niestandardowe mają interfejs deklaratywny API (np. atrybut percentage pierścienia postępu), działają dobrze z lit-html, jak widać na liście poniżej.

<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 odpowiednią klasę Page, która wypełnia znaczniki znaczników lit-html, zapewniając implementacje elementów 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 osobnym pliku 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 ustawień centralnych, takich jak font-family lub box-sizing, nie trzeba deklarować wielokrotnie. Tutaj definiowane są też motywy i opcje trybu ciemnego. Poniżej znajduje się lista reguł dotyczących strony Ustawienia, która zawiera 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;
}
Formularz w układzie siatki na stronie ustawień aplikacji HIIT Time.
Każda strona to osobny świat. Stylizacja odbywa się bezpośrednio w nazwach 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 GitHub. Możesz odtworzyć demo w nowym oknie lub wbudowanym iframe poniżej, który symuluje urządzenie mobilne.

Podziękowania

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