Скевоморфная попытка воссоздать солнечный калькулятор в Интернете с помощью API-интерфейса Window Controls Overlay и API датчика внешней освещенности.
Задача
Я ребенок 1980-х годов. Когда я учился в старшей школе, в моде были солнечные калькуляторы. В школе нам всем подарили TI-30X SOLAR , и у меня остались приятные воспоминания о том, как мы сравнивали наши калькуляторы друг с другом, вычисляя факториал 69 — наибольшее число, с которым мог справиться TI-30X. (Отклонение скорости было очень измеримым, я до сих пор понятия не имею, почему.)
Теперь, почти 28 лет спустя, я подумал, что было бы забавно воссоздать калькулятор в HTML, CSS и JavaScript. Будучи не очень хорошим дизайнером, я начал не с нуля, а с CodePen от Sassja Ceballos .
Сделайте его доступным для установки
Хотя начало было неплохим, я решил прокачать его до полной скевоморфной крутости. Первым шагом было сделать его PWA, чтобы его можно было установить. Я поддерживаю базовый шаблон PWA на Glitch , который я переделываю всякий раз, когда мне нужна быстрая демонстрация. Его сервисный работник не принесет вам никаких наград в области кодирования, и он определенно не готов к работе, но этого достаточно, чтобы вызвать мини-информационную панель Chromium, чтобы приложение можно было установить.
self.addEventListener('install', (event) => {
self.skipWaiting();
});
self.addEventListener('activate', (event) => {
self.clients.claim();
event.waitUntil(
(async () => {
if ('navigationPreload' in self.registration) {
await self.registration.navigationPreload.enable();
}
})(),
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
(async () => {
try {
const response = await event.preloadResponse;
if (response) {
return response;
}
return fetch(event.request);
} catch {
return new Response('Offline');
}
})(),
);
});
Смешение с мобильным устройством
Теперь, когда приложение можно установить, следующим шагом будет его максимальное совмещение с приложениями операционной системы. На мобильном устройстве я могу сделать это, установив fullscreen
режим отображения в манифесте веб-приложения.
{
"display": "fullscreen"
}
На устройствах с отверстием или выемкой для камеры настройка области просмотра так, чтобы контент занимал весь экран, придала приложению великолепный вид.
<meta name="viewport" content="initial-scale=1, viewport-fit=cover" />
Смешение с рабочим столом
На рабочем столе есть интересная функция, которую я могу использовать: Window Controls Overlay , которая позволяет мне помещать контент в строку заголовка окна приложения. Первым шагом является переопределение последовательности возврата к режиму отображения, чтобы он сначала пытался использовать window-controls-overlay
, когда он доступен.
{
"display_override": ["window-controls-overlay"]
}
Это приводит к тому, что строка заголовка фактически исчезает, а содержимое перемещается вверх в область строки заголовка, как если бы строки заголовка там не было. Моя идея состоит в том, чтобы переместить скевоморфный солнечный элемент вверх в строку заголовка, а остальную часть пользовательского интерфейса калькулятора соответственно вниз, что я могу сделать с помощью CSS, который использует переменные среды titlebar-area-*
. Вы заметите, что все селекторы имеют класс wco
, который будет актуален парой абзацев ниже.
#calc_solar_cell.wco {
position: fixed;
left: calc(0.25rem + env(titlebar-area-x, 0));
top: calc(0.75rem + env(titlebar-area-y, 0));
width: calc(env(titlebar-area-width, 100%) - 0.5rem);
height: calc(env(titlebar-area-height, 33px) - 0.5rem);
}
#calc_display_surface.wco {
margin-top: calc(env(titlebar-area-height, 33px) - 0.5rem);
}
Далее мне нужно решить, какие элементы сделать перетаскиваемыми, поскольку строка заголовка, которую я обычно использую для перетаскивания, недоступна. В стиле классического виджета я могу даже сделать весь калькулятор перетаскиваемым, применив (-webkit-)app-region: drag
, кроме кнопок, которые получают (-webkit-)app-region: no-drag
чтобы они нельзя использовать для перетаскивания.
#calc_inside.wco,
#calc_solar_cell.wco {
-webkit-app-region: drag;
app-region: drag;
}
button {
-webkit-app-region: no-drag;
app-region: no-drag;
}
Последний шаг — сделать приложение реагирующим на изменения наложения оконных элементов управления. При истинном подходе к прогрессивному улучшению я загружаю код этой функции только тогда, когда браузер ее поддерживает.
if ('windowControlsOverlay' in navigator) {
import('/wco.js');
}
Всякий раз, когда изменяется геометрия наложения элементов управления окна, я модифицирую приложение, чтобы оно выглядело как можно более естественно. Хорошей идеей является устранение этого события, поскольку оно может часто срабатывать, когда пользователь изменяет размер окна. А именно, я применяю класс wco
к некоторым элементам, поэтому мой CSS, приведенный выше, срабатывает, а также меняю цвет темы. Я могу определить, видимо ли наложение элементов управления окном, проверив свойство navigator.windowControlsOverlay.visible
.
const meta = document.querySelector('meta[name="theme-color"]');
const nodes = document.querySelectorAll(
'#calc_display_surface, #calc_solar_cell, #calc_outside, #calc_inside',
);
const toggleWCO = () => {
if (!navigator.windowControlsOverlay.visible) {
meta.content = '';
} else {
meta.content = '#385975';
}
nodes.forEach((node) => {
node.classList.toggle('wco', navigator.windowControlsOverlay.visible);
});
};
const debounce = (func, wait) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
navigator.windowControlsOverlay.ongeometrychange = debounce((e) => {
toggleWCO();
}, 250);
toggleWCO();
Теперь, когда все это готово, я получаю виджет калькулятора, который выглядит почти как классический Winamp с одной из тем старой школы Winamp . Теперь я могу свободно разместить калькулятор на своем рабочем столе и активировать функцию управления окном, щелкнув шеврон в правом верхнем углу.
Реально работающий солнечный элемент
Для полного чудачества мне, конечно, нужно было заставить солнечную батарею действительно работать. Калькулятор должен работать только при достаточном освещении. Я смоделировал это, установив opacity
CSS цифр на дисплее с помощью переменной CSS --opacity
, которой я управляю через JavaScript.
:root {
--opacity: 0.75;
}
#calc_expression,
#calc_result {
opacity: var(--opacity);
}
Чтобы определить, достаточно ли света для работы калькулятора, я использую API AmbientLightSensor
. Чтобы этот API был доступен, мне нужно было установить флаг #enable-generic-sensor-extra-classes
в about:flags
и запросить разрешение 'ambient-light-sensor'
. Как и раньше, я использую прогрессивное улучшение, чтобы загружать соответствующий код только тогда, когда поддерживается API.
if ('AmbientLightSensor' in window) {
import('/als.js');
}
Датчик возвращает значение окружающего освещения в люксах всякий раз, когда доступны новые показания. На основе таблицы значений типичных ситуаций освещения я придумал очень простую формулу для преобразования значения люкс в значение от 0 до 1, которое я программно присваиваю переменной --opacity
.
const luxToOpacity = (lux) => {
if (lux > 250) {
return 1;
}
return lux / 250;
};
const sensor = new window.AmbientLightSensor();
sensor.onreading = () => {
console.log('Current light level:', sensor.illuminance);
document.documentElement.style.setProperty(
'--opacity',
luxToOpacity(sensor.illuminance),
);
};
sensor.onerror = (event) => {
console.log(event.error.name, event.error.message);
};
(async () => {
const {state} = await navigator.permissions.query({
name: 'ambient-light-sensor',
});
if (state === 'granted') {
sensor.start();
}
})();
На видео ниже вы можете увидеть, как калькулятор начинает работать, когда я включаю достаточное освещение в комнате. И вот он: скевоморфный солнечный калькулятор, который действительно работает. Мой старый добрый проверенный временем TI-30X SOLAR действительно прошел долгий путь.
Демо
Обязательно поиграйтесь с демо-версией Designcember Calculator и ознакомьтесь с исходным кодом на Glitch . (Чтобы установить приложение, вам необходимо открыть его в отдельном окне. Встроенная версия, указанная ниже, не будет запускать мини-информационную панель.)
Счастливого дизайнерского декабря !