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.


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ę.
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).

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>

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

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

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.