Lingua

Contattami

App Android · dal 2025

ClearLap

Cronometro Android con tempi sul giro, realizzato come un'unica pagina web offline in un guscio Cordova.

Rilasciato
Permessi di runtime
Nessuno
Pubblicità
Nessuno
Prezzo
Gratuito
  • HTML
  • CSS
  • JavaScript
  • Apache Cordova (Android)
  • Web Audio API
  • Screen Wake Lock API
  • Vibration API
Google Play play.google.com

Chi sono

ClearLap è un cronometro con giri, evidenziazione del giro migliore e peggiore, suoni al tocco, vibrazione dove la WebView la supporta e un layout scuro a schermo intero. Non ha pubblicità, account, uso della rete né plugin: l'intera app è un unico file HTML di circa 8 KB con CSS e JavaScript inline.

Il lavoro di ingegneria sta nel fare correttamente le cose di base dentro una WebView: un tempo che non deriva quando i frame vengono saltati o l'app è in background, giri che sommati danno sempre il totale, un wake lock dello schermo richiesto dove la WebView lo supporta e suoni di feedback sintetizzati senza file audio.

ClearLap · cronometro

cosa è stato risolto

ATTIVOdal 2025

Nucleo di misurazione del tempo

nel prodotto

Cosa offreIl tempo resta esatto quando il telefono nasconde l'app, la pausa non aggiunge né perde tempo, i giri sommati danno il totale e le cifre non saltellano.

Problema
Un cronometro in una WebView non può fidarsi di timer o callback dei frame per contare il tempo: vengono limitati, saltati o messi in pausa quando l'app è in background.
Soluzione

Il tempo viene sempre calcolato dai timestamp; il ciclo dei frame si limita a disegnare. Pausa, ripresa e giri sono aritmetica sui timestamp memorizzati.

Stack
  • JavaScript
  • requestAnimationFrame
Attività · 5
I contatori a intervallo derivano e si fermano mentre l'app è nascostaIl tempo trascorso è Date.now() meno un timestamp di avvio, ricalcolato a ogni frame

tick() non incrementa mai un contatore. Imposta elapsed = Date.now() - startTs e ridisegna. Se i frame vengono saltati o la WebView sospende requestAnimationFrame in background, il frame successivo mostra il tempo corretto perché nulla è stato accumulato.

Pausa e ripresa non devono perdere né aggiungere tempoAlla ripresa si ribasa l'avvio: startTs = Date.now() - elapsed

La pausa annulla il frame di animazione e mantiene elapsed così com'è. La ripresa sposta startTs indietro della quantità già trascorsa, quindi la formula in tick() prosegue dallo stesso valore senza una variabile separata per la «durata in pausa».

Il reset annulla il ciclo dei frame, azzera lo stato e disabilita il pulsante Giro; anche doLap() esce subito quando il cronometro non è in funzione.

Le durate dei giri devono sommarsi esattamente al tempo totaleOgni giro è il tempo trascorso meno il segno del giro precedente, così i giri si telescopizzano nel totale

doLap() memorizza il numero del giro, la sua durata (elapsed - lapElapsed) e il totale progressivo, poi sposta lapElapsed su elapsed. Poiché ogni giro inizia dove è finito il precedente, la somma delle durate dei giri è uguale al totale per costruzione, senza scarti di arrotondamento.

Il contatore del giro corrente sotto il tempo principale usa lo stesso segno: elapsed - lapElapsed.

Segnare il giro migliore e peggiore non ha senso con un solo giroCalcolare minimo e massimo su tutti i giri a ogni rendering; evidenziare solo quando sono almeno 2

renderLaps() ricostruisce l'elenco dal più recente, trova le durate più breve e più lunga e aggiunge le classi best/worst solo quando esiste più di un giro.

Le cifre si spostano lateralmente al variare del tempoFont monospazio più font-variant-numeric: tabular-nums su ogni display del tempo

Tutti i campi del tempo usano cifre a larghezza fissa, così il display a 16vw non si sposta al cambiare dei centesimi di secondo. fmt() stampa sempre mm:ss.cc con riempimento di zeri.

Comportamento del dispositivo senza plugin

nel prodotto

Cosa offreLo schermo resta acceso durante le sessioni lunghe, l'app riempie tutto lo schermo e i tocchi producono un bip e una vibrazione, senza mai disturbare la misurazione del tempo.

Problema
Schermo acceso, schermo intero, suono e feedback tattile di solito arrivano da plugin Cordova, e i browser ne consentono alcuni solo dopo un gesto dell'utente.
Soluzione

Usare le API della piattaforma web dalla WebView dove esistono, richiederle al momento giusto e renderle tutte facoltative, così un'API mancante non rompe mai la misurazione del tempo.

Stack
  • Screen Wake Lock API
  • Fullscreen API
  • Web Audio API
  • Vibration API
  • Cordova config.xml
Attività · 4
Lo schermo si spegne durante una lunga sessione di misurazioneRichiedere un wake lock dello schermo dove la WebView lo supporta, e di nuovo ogni volta che la pagina torna visibile

Il sistema rilascia un wake lock quando la pagina viene nascosta. requestWakeLock() viene eseguito al caricamento della pagina e a ogni visibilitychange verso visible, e il listener di rilascio azzera il riferimento. La chiamata viene saltata quando navigator.wakeLock è assente e gli errori vengono ignorati, così il cronometro funziona allo stesso modo in entrambi i casi.

Lo schermo intero può essere richiesto solo da un gesto dell'utenteRichiederlo al primo tocco, con un fallback webkit, poi rimuovere il listener

Un listener touchstart monouso chiama requestFullscreen o webkitRequestFullscreen e si rimuove. config.xml imposta inoltre la preferenza Fullscreen di Cordova per la build Android.

Suoni al tocco senza file audio e senza clic o scoppiettiiSintetizzare brevi bip sinusoidali con Web Audio e una dissolvenza esponenziale del guadagno

beep() crea un oscillatore e un nodo di guadagno a ogni pressione e porta il guadagno in modo esponenziale a 0.001 per la durata del bip, evitando lo scoppio di un arresto brusco. Avvio, giro e reset usano altezze e durate diverse (880, 1100 e 440 Hz).

L'AudioContext viene creato in modo lazy dentro il gestore del primo pulsante, il che soddisfa la regola dell'autoplay secondo cui l'audio deve partire da un gesto dell'utente.

Gli errori del feedback non devono mai interrompere la misurazione del tempoSuono e vibrazione vengono eseguiti prima dei cambi di stato, solo dove l'API esiste, e dentro try/catch

beep() e vibe() intercettano ogni eccezione, e vibe() chiama navigator.vibrate solo dove è disponibile. Avvio, giro e reset richiedono durate di vibrazione diverse (25, 15 e 40 ms).

config.xml non dichiara alcun plugin e la pagina non effettua chiamate di rete, quindi il nucleo di misurazione del tempo dipende solo dalla WebView.

Numeri

dal prodotto
  • Funziona su Android 5.0 e successivi
  • Codice dell'app circa 8 KB Un solo file HTML, nessun framework.
  • Precisione 1/100 s