ClearLap
Chronomètre Android avec temps de tour, conçu comme une seule page web hors ligne dans une enveloppe Cordova.
- Sortie
- Autorisations d'exécution
- Aucune
- Publicités
- Aucune
- Prix
- Gratuit
À propos
ClearLap est un chronomètre avec tours, mise en évidence du meilleur et du pire tour, sons de clic, vibration lorsque la WebView la prend en charge, et une présentation sombre en plein écran. Il n'a ni publicités, ni comptes, ni utilisation du réseau, ni plugins : toute l'application tient dans un seul fichier HTML d'environ 8 KB avec CSS et JavaScript en ligne.
Le travail d'ingénierie consiste à faire correctement les bases dans une WebView : un temps qui ne dérive pas lorsque des images sont perdues ou que l'application est en arrière-plan, des tours dont la somme égale toujours le total, un verrou d'éveil de l'écran demandé là où la WebView le prend en charge, et des sons de retour synthétisés sans fichiers audio.
ClearLap · chronomètre
ce qui a été résolu
Cœur de mesure du temps
dans le produitCe que cela apporteLe temps reste exact lorsque le téléphone masque l'application, une pause ne fait jamais perdre ni gagner de temps, les tours s'additionnent au total et les chiffres ne sautent pas.
- Problème
- Un chronomètre dans une WebView ne peut pas se fier aux minuteurs ni aux callbacks d'image pour compter le temps : ils sont ralentis, sautés ou suspendus lorsque l'application est en arrière-plan.
- Solution
Le temps est toujours calculé à partir d'horodatages ; la boucle d'images ne fait que dessiner. La pause, la reprise et les tours sont de l'arithmétique sur des horodatages stockés.
- Stack
Les compteurs à intervalle dérivent et s'arrêtent pendant que l'application est masquéeLe temps écoulé est Date.now() moins un horodatage de départ, recalculé à chaque image
tick() n'incrémente jamais un compteur. Il définit elapsed = Date.now() - startTs et redessine. Si des images sont perdues ou si la WebView suspend requestAnimationFrame en arrière-plan, l'image suivante affiche le bon temps, car rien n'a été accumulé.
La pause et la reprise ne doivent ni faire perdre ni ajouter du tempsÀ la reprise, on rebase le départ : startTs = Date.now() - elapsed
La pause annule l'image d'animation et conserve elapsed tel quel. La reprise recule startTs de la durée déjà écoulée, de sorte que la formule de tick() repart de la même valeur sans variable distincte de « durée de pause ».
La remise à zéro annule la boucle d'images, réinitialise l'état et désactive le bouton Tour ; doLap() retourne aussi immédiatement lorsque le chronomètre ne tourne pas.
Les durées des tours doivent s'additionner exactement au temps totalChaque tour est le temps écoulé moins la marque du tour précédent, donc les tours se télescopent jusqu'au total
doLap() stocke le numéro du tour, sa durée (elapsed - lapElapsed) et le total cumulé, puis déplace lapElapsed sur elapsed. Comme chaque tour commence là où le précédent s'est terminé, la somme des durées des tours égale le total par construction, sans écart d'arrondi.
Le compteur du tour en cours sous le temps principal utilise la même marque : elapsed - lapElapsed.
Marquer le meilleur et le pire tour n'a aucun sens avec un seul tourCalculer le minimum et le maximum sur tous les tours à chaque affichage ; ne mettre en évidence que s'il y en a 2 ou plus
renderLaps() reconstruit la liste en commençant par le plus récent, trouve les durées la plus courte et la plus longue et n'ajoute les classes best/worst que s'il existe plus d'un tour.
Les chiffres se décalent latéralement quand le temps changePolice à chasse fixe plus font-variant-numeric: tabular-nums sur chaque affichage du temps
Tous les champs de temps utilisent des chiffres de largeur fixe, de sorte que l'affichage à 16vw ne bouge pas quand les centièmes changent. fmt() affiche toujours mm:ss.cc avec des zéros de remplissage.
Comportement de l'appareil sans plugins
dans le produitCe que cela apporteL'écran reste allumé pendant les longues séances, l'application occupe tout l'écran, et les appuis donnent un bip et une vibration, sans jamais perturber la mesure du temps.
- Problème
- L'écran allumé, le plein écran, le son et le retour haptique viennent d'ordinaire de plugins Cordova, et les navigateurs n'en autorisent certains qu'après un geste de l'utilisateur.
- Solution
Utiliser les API de la plateforme web depuis la WebView là où elles existent, les demander au bon moment, et rendre chacune facultative pour qu'une API manquante ne perturbe jamais la mesure du temps.
- Stack
L'écran s'éteint pendant une longue séance de mesureDemander un verrou d'éveil de l'écran là où la WebView le prend en charge, et de nouveau chaque fois que la page redevient visible
Le système libère un verrou d'éveil lorsque la page est masquée. requestWakeLock() s'exécute au chargement de la page et à chaque visibilitychange vers visible, et l'écouteur de libération efface la référence. L'appel est ignoré lorsque navigator.wakeLock est absent et les erreurs sont absorbées, de sorte que le chronomètre fonctionne de la même façon dans les deux cas.
Le plein écran ne peut être demandé que depuis un geste de l'utilisateurLe demander au premier toucher, avec un repli webkit, puis retirer l'écouteur
Un écouteur touchstart à usage unique appelle requestFullscreen ou webkitRequestFullscreen et se retire lui-même. config.xml définit aussi la préférence Cordova Fullscreen pour la build Android.
Des sons de clic sans fichiers audio et sans claquements ni craquementsSynthétiser de courts bips sinusoïdaux avec Web Audio et un fondu de gain exponentiel
beep() crée un oscillateur et un nœud de gain à chaque appui et ramène le gain de façon exponentielle à 0.001 sur la durée du bip, ce qui évite le claquement d'un arrêt brutal. Le démarrage, le tour et la remise à zéro utilisent des hauteurs et des durées différentes (880, 1100 et 440 Hz).
L'AudioContext est créé à la demande dans le premier gestionnaire de bouton, ce qui satisfait la règle de lecture automatique selon laquelle l'audio doit démarrer depuis un geste de l'utilisateur.
Les échecs du retour sensoriel ne doivent jamais interrompre la mesure du tempsLe son et la vibration s'exécutent avant les changements d'état, seulement là où l'API existe, et dans un try/catch
beep() et vibe() interceptent toutes les exceptions, et vibe() n'appelle navigator.vibrate que là où il est disponible. Le démarrage, le tour et la remise à zéro demandent des durées de vibration différentes (25, 15 et 40 ms).
config.xml ne déclare aucun plugin et la page n'effectue aucun appel réseau, de sorte que le cœur de mesure du temps ne dépend que de la WebView.
Chiffres
du produit- Fonctionne sur Android 5.0 et versions ultérieures
- Code de l'application environ 8 KB Un seul fichier HTML, aucun framework.
- Précision 1/100 s