Język

Napisz do mnie

Aplikacja na Android · od 2025

ClearLap

Stoper na Android z czasami okrążeń, zbudowany jako jedna strona działająca offline w powłoce Cordova.

Wydany
Uprawnienia w czasie działania
Brak
Reklamy
Brak
Cena
Bezpłatny
  • HTML
  • CSS
  • JavaScript
  • Apache Cordova (Android)
  • Web Audio API
  • Screen Wake Lock API
  • Vibration API
Google Play play.google.com

O mnie

ClearLap to stoper z okrążeniami, wyróżnianiem najlepszego i najgorszego okrążenia, dźwiękami kliknięć, wibracją tam, gdzie WebView ją obsługuje, oraz ciemnym układem na pełnym ekranie. Nie ma reklam, kont, użycia sieci ani wtyczek: cała aplikacja to jeden plik HTML o wielkości około 8 KB z wbudowanym CSS i JavaScript.

Praca inżynieryjna polega na poprawnym wykonaniu podstaw wewnątrz WebView: czas, który nie dryfuje, gdy gubione są klatki lub aplikacja działa w tle; okrążenia, które zawsze sumują się do całości; blokada wygaszania ekranu żądana tam, gdzie WebView ją obsługuje; oraz dźwięki zwrotne syntezowane bez plików audio.

ClearLap · stoper

co rozwiązano

AKTYWNYod 2025

Rdzeń pomiaru czasu

w produkcie

Co to dajeCzas pozostaje dokładny, gdy telefon ukrywa aplikację, pauza nigdy nie dodaje ani nie gubi czasu, okrążenia sumują się do całości, a cyfry nie skaczą.

Problem
Stoper w WebView nie może polegać na timerach ani callbackach klatek przy liczeniu czasu: są dławione, pomijane lub wstrzymywane, gdy aplikacja działa w tle.
Rozwiązanie

Czas jest zawsze obliczany ze znaczników czasu; pętla klatek tylko rysuje. Pauza, wznowienie i okrążenia to arytmetyka na zapisanych znacznikach czasu.

Technologie
  • JavaScript
  • requestAnimationFrame
Zadania · 5
Liczniki oparte na interwałach dryfują i zatrzymują się, gdy aplikacja jest ukrytaUpłynięty czas to Date.now() minus znacznik czasu startu, przeliczany w każdej klatce

tick() nigdy nie zwiększa licznika. Ustawia elapsed = Date.now() - startTs i przerysowuje. Jeśli klatki są gubione albo WebView wstrzymuje requestAnimationFrame w tle, następna klatka pokazuje poprawny czas, bo nic nie było kumulowane.

Pauza i wznowienie nie mogą gubić ani dodawać czasuPrzy wznowieniu przesuwamy start: startTs = Date.now() - elapsed

Pauza anuluje klatkę animacji i zostawia elapsed bez zmian. Wznowienie cofa startTs o już upłynięty czas, więc wzór w tick() kontynuuje od tej samej wartości, bez osobnej zmiennej z czasem pauzy.

Reset anuluje pętlę klatek, zeruje stan i wyłącza przycisk Lap; doLap() także kończy się wcześniej, gdy stoper nie działa.

Czasy okrążeń muszą sumować się dokładnie do czasu całkowitegoKażde okrążenie to elapsed minus poprzedni znacznik okrążenia, więc okrążenia sumują się teleskopowo do całości

doLap() zapisuje numer okrążenia, jego czas (elapsed - lapElapsed) i bieżącą sumę, po czym przesuwa lapElapsed na elapsed. Ponieważ każde okrążenie zaczyna się tam, gdzie skończyło się poprzednie, suma czasów okrążeń z konstrukcji równa się całości, bez luki zaokrągleń.

Licznik bieżącego okrążenia pod głównym czasem używa tego samego znacznika: elapsed - lapElapsed.

Oznaczanie najlepszego i najgorszego okrążenia nie ma sensu przy jednym okrążeniuPrzy każdym renderze liczymy min i max ze wszystkich okrążeń; wyróżniamy tylko przy 2 lub więcej

renderLaps() odbudowuje listę od najnowszego, znajduje najkrótszy i najdłuższy czas i dodaje klasy best/worst tylko wtedy, gdy istnieje więcej niż jedno okrążenie.

Cyfry skaczą na boki, gdy zmienia się czasCzcionka monospace plus font-variant-numeric: tabular-nums na każdym wyświetlaczu czasu

Wszystkie pola czasu używają cyfr o stałej szerokości, więc wyświetlacz przy 16vw nie przesuwa się, gdy zmieniają się setne części sekundy. fmt() zawsze wypisuje mm:ss.cc z dopełnieniem zerami.

Zachowanie urządzenia bez wtyczek

w produkcie

Co to dajeEkran pozostaje włączony podczas długich sesji, aplikacja wypełnia cały ekran, a dotknięcia dają sygnał dźwiękowy i wibrację, nigdy nie zakłócając pomiaru czasu.

Problem
Podtrzymanie ekranu, pełny ekran, dźwięk i haptyka zwykle pochodzą z wtyczek Cordova, a przeglądarki pozwalają na część z nich dopiero po geście użytkownika.
Rozwiązanie

Używamy API platformy webowej z WebView tam, gdzie istnieją, żądamy ich w odpowiednim momencie i czynimy każde opcjonalnym, by brakujące API nigdy nie psuło pomiaru czasu.

Technologie
  • Screen Wake Lock API
  • Fullscreen API
  • Web Audio API
  • Vibration API
  • Cordova config.xml
Zadania · 4
Ekran wyłącza się podczas długiej sesji pomiaruŻądamy blokady wygaszania ekranu tam, gdzie WebView ją obsługuje, i ponownie za każdym razem, gdy strona staje się widoczna

System zwalnia blokadę, gdy strona jest ukryta. requestWakeLock() uruchamia się przy ładowaniu strony i przy każdym visibilitychange na visible, a listener zwolnienia czyści referencję. Wywołanie jest pomijane, gdy brakuje navigator.wakeLock, a błędy są połykane, więc stoper działa tak samo w obu przypadkach.

Pełny ekran można żądać tylko z gestu użytkownikaŻądamy go przy pierwszym dotknięciu, z fallbackiem webkit, a potem usuwamy listener

Jednorazowy listener touchstart wywołuje requestFullscreen lub webkitRequestFullscreen i sam się usuwa. config.xml ustawia też preferencję Cordova Fullscreen dla buildu na Android.

Dźwięki kliknięć bez plików audio i bez trzaskówSyntezujemy krótkie sinusoidalne beepy w Web Audio z wykładniczym wygaszaniem wzmocnienia

beep() tworzy oscylator i węzeł wzmocnienia przy każdym naciśnięciu i wykładniczo sprowadza wzmocnienie do 0.001 przez czas trwania beepa, co pozwala uniknąć trzasku nagłego zatrzymania. Start, okrążenie i reset używają różnych wysokości i długości (880, 1100 i 440 Hz).

AudioContext jest tworzony leniwie w pierwszym handlerze przycisku, co spełnia regułę autoplay, że dźwięk musi startować z gestu użytkownika.

Awarie sprzężenia zwrotnego nigdy nie mogą przerwać pomiaru czasuDźwięk i wibracja uruchamiają się przed zmianami stanu, tylko tam, gdzie API istnieje, i wewnątrz try/catch

beep() i vibe() łapią każdy wyjątek, a vibe() wywołuje navigator.vibrate tylko tam, gdzie jest dostępne. Start, okrążenie i reset żądają różnych długości wibracji (25, 15 i 40 ms).

config.xml nie deklaruje żadnych wtyczek, a strona nie wykonuje wywołań sieciowych, więc rdzeń pomiaru czasu zależy tylko od WebView.

Liczby

z produktu
  • Działa na Android 5.0 i nowszych
  • Kod aplikacji około 8 KB Jeden plik HTML, bez frameworków.
  • Precyzja 1/100 s