ClearLap
Секундомір для Android із часом кіл, зроблений як одна офлайн-сторінка в оболонці Cordova.
- Випущено
- Дозволи під час роботи
- Немає
- Реклама
- Немає
- Ціна
- Безкоштовно
Про мене
ClearLap — це секундомір із колами, підсвічуванням найкращого й найгіршого кола, звуками натискань, вібрацією там, де її підтримує WebView, і темним інтерфейсом на весь екран. У ньому немає реклами, облікових записів, використання мережі та плагінів: увесь застосунок — це один HTML-файл приблизно на 8 КБ із вбудованими CSS і JavaScript.
Інженерна робота полягає в тому, щоб правильно зробити базові речі всередині WebView: час, який не дрейфує, коли пропускаються кадри або застосунок у фоні; кола, які завжди сумуються до загального часу; блокування вимкнення екрана, що запитується там, де WebView його підтримує; і звуки зворотного зв'язку, синтезовані без аудіофайлів.
ClearLap · секундомір
що розв'язано
Ядро відліку часу
у продуктіЩо це даєЧас лишається точним, коли телефон ховає застосунок, пауза ніколи не додає й не втрачає час, кола сумуються до загального часу, а цифри не стрибають.
- Проблема
- Секундомір у WebView не може покладатися на таймери чи колбеки кадрів для відліку часу: їх уповільнюють, пропускають або призупиняють, коли застосунок у фоні.
- Рішення
Час завжди обчислюється з міток часу; цикл кадрів лише малює. Пауза, продовження й кола — це арифметика над збереженими мітками часу.
- Стек
Лічильники на інтервалах дрейфують і зупиняються, поки застосунок прихованийМинулий час — це Date.now() мінус мітка початку, що перераховується на кожному кадрі
tick() ніколи не збільшує лічильник. Він встановлює elapsed = Date.now() - startTs і перемальовує. Якщо кадри пропущено або WebView призупиняє requestAnimationFrame у фоні, наступний кадр покаже правильний час, бо нічого не накопичувалося.
Пауза й продовження не повинні втрачати чи додавати часПід час продовження початок переносять: startTs = Date.now() - elapsed
Пауза скасовує кадр анімації й лишає elapsed без змін. Продовження зсуває startTs назад на вже минулий час, тож формула в tick() продовжується з того самого значення без окремої змінної «тривалість паузи».
Скидання скасовує цикл кадрів, обнуляє стан і вимикає кнопку Lap; doLap() також одразу виходить, коли секундомір не запущено.
Тривалості кіл мають точно сумуватися до загального часуКожне коло — це elapsed мінус попередня позначка кола, тож кола телескопічно сумуються до загального часу
doLap() зберігає номер кола, його тривалість (elapsed - lapElapsed) і поточну суму, потім переносить lapElapsed на elapsed. Оскільки кожне коло починається там, де закінчилося попереднє, сума тривалостей кіл за побудовою дорівнює загальному часу, без розбіжності через округлення.
Лічильник поточного кола під основним часом використовує ту саму позначку: elapsed - lapElapsed.
Позначення найкращого й найгіршого кола не має сенсу, коли коло однеНа кожному рендері обчислювати мінімум і максимум за всіма колами; підсвічувати лише тоді, коли їх 2 і більше
renderLaps() перебудовує список від найновішого, знаходить найкоротшу й найдовшу тривалості та додає класи best/worst лише тоді, коли кіл більше одного.
Цифри стрибають убік, коли змінюється часМоноширинний шрифт плюс font-variant-numeric: tabular-nums на кожному відображенні часу
Усі поля часу використовують цифри фіксованої ширини, тож відображення розміром 16vw не зсувається, коли змінюються соті частки секунди. fmt() завжди виводить mm:ss.cc з нулями попереду.
Поведінка пристрою без плагінів
у продуктіЩо це даєЕкран лишається ввімкненим під час довгих сесій, застосунок займає весь екран, а натискання супроводжуються звуком і вібрацією, ніколи не заважаючи відліку часу.
- Проблема
- Увімкнений екран, повноекранний режим, звук і тактильний відгук зазвичай надходять із плагінів Cordova, а браузери дозволяють частину з них лише після жесту користувача.
- Рішення
Використовувати API вебплатформи з WebView там, де вони є, запитувати їх у потрібний момент і зробити кожен із них необов'язковим, щоб відсутній API ніколи не ламав відлік часу.
- Стек
Екран гасне під час довгого сеансу вимірюванняЗапитувати блокування вимкнення екрана там, де WebView його підтримує, і знову щоразу, коли сторінка стає видимою
Система знімає wake lock, коли сторінку приховано. requestWakeLock() запускається під час завантаження сторінки й на кожному visibilitychange до стану visible, а слухач звільнення очищає посилання. Виклик пропускається, якщо navigator.wakeLock відсутній, а помилки поглинаються, тож секундомір працює однаково в обох випадках.
Повноекранний режим можна запросити лише з жесту користувачаЗапитувати його при першому дотику, із запасним варіантом webkit, а потім прибрати слухач
Одноразовий слухач touchstart викликає requestFullscreen або webkitRequestFullscreen і сам себе видаляє. config.xml також задає параметр Cordova Fullscreen для збірки під Android.
Звуки натискань без аудіофайлів і без клацань чи тріскуСинтезувати короткі синусоїдні сигнали через Web Audio з експоненційним згасанням підсилення
beep() створює осцилятор і вузол підсилення на кожне натискання й експоненційно знижує підсилення до 0.001 за тривалість сигналу, що уникає тріску від різкої зупинки. Старт, коло й скидання використовують різні висоти й тривалості (880, 1100 і 440 Гц).
AudioContext створюється ліниво всередині обробника першої кнопки, що відповідає правилу автовідтворення: звук має починатися з жесту користувача.
Збої зворотного зв'язку ніколи не повинні переривати відлік часуЗвук і вібрація запускаються перед змінами стану, лише там, де API існує, і всередині try/catch
beep() і vibe() перехоплюють усі винятки, а vibe() викликає navigator.vibrate лише там, де він доступний. Старт, коло й скидання запитують різну тривалість вібрації (25, 15 і 40 мс).
config.xml не оголошує жодних плагінів, а сторінка не робить мережевих викликів, тож ядро відліку часу залежить лише від WebView.
Цифри
з продукту- Працює на Android 5.0 і новіше
- Код застосунку приблизно 8 КБ Один HTML-файл, без фреймворків.
- Точність 1/100 с