Tentative skeuomorphique de recréer un calculateur solaire sur le Web avec l'API Window Controls Overlay et l'API Ambient Light Sensor.
Problématique
Je suis un enfant des années 1980. Quand j'étais au lycée, les calculatrices solaires étaient très populaires. L'école nous avait tous donné une TI-30X SOLAR. Je me souviens avec émotion de nos calculatrices que nous comparions en calculant la factorielle de 69, le nombre le plus élevé que la TI-30X pouvait gérer. (La variance de vitesse était très mesurable, je ne sais toujours pas pourquoi.)
Près de 28 ans plus tard, je me suis dit que ce serait un défi Designcember amusant de recréer la calculatrice en HTML, CSS et JavaScript. N'étant pas un grand concepteur, je n'ai pas commencé à partir de zéro, mais avec un CodePen de Sassja Ceballos.
Rendre l'application installable
Bien que ce ne soit pas un mauvais début, j'ai décidé de le rendre encore plus skeuomorphique. La première étape consistait à en faire une PWA pour qu'elle puisse être installée.
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');
}
})(),
);
});
Fusionner avec un appareil mobile
Maintenant que l'application est installable, l'étape suivante consiste à la faire se fondre autant que possible dans les applications du système d'exploitation. Sur mobile, je peux le faire en définissant le mode d'affichage sur fullscreen
dans le fichier manifeste de l'application Web.
{
"display": "fullscreen"
}
Sur les appareils dotés d'un trou ou d'une encoche pour l'appareil photo, ajuster la fenêtre d'affichage pour que le contenu couvre l'intégralité de l'écran rend l'application magnifique.
<meta name="viewport" content="initial-scale=1, viewport-fit=cover" />
Intégration à l'ordinateur
Sur ordinateur, il existe une fonctionnalité intéressante que je peux utiliser : Window Controls Overlay, qui me permet de placer du contenu dans la barre de titre de la fenêtre de l'application. La première étape consiste à remplacer la séquence de secours du mode d'affichage afin qu'elle tente d'utiliser window-controls-overlay
en premier lorsqu'il est disponible.
{
"display_override": ["window-controls-overlay"]
}
La barre de titre disparaît et le contenu remonte dans la zone de la barre de titre comme si elle n'existait pas. Mon idée est de déplacer la cellule solaire skeuomorphique dans la barre de titre et le reste de l'interface utilisateur de la calculatrice vers le bas en conséquence, ce que je peux faire avec du CSS qui utilise les variables d'environnement titlebar-area-*
. Vous remarquerez que tous les sélecteurs comportent une classe wco
, qui sera pertinente dans quelques paragraphes.
#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);
}
Ensuite, je dois décider quels éléments rendre déplaçables, car la barre de titre que j'utiliserais habituellement pour le déplacement n'est pas disponible. Dans le style d'un widget classique, je peux même rendre l'ensemble de la calculatrice déplaçable en appliquant (-webkit-)app-region: drag
, à l'exception des boutons, qui obtiennent (-webkit-)app-region: no-drag
afin qu'ils ne puissent pas être utilisés pour le déplacement.
#calc_inside.wco,
#calc_solar_cell.wco {
-webkit-app-region: drag;
app-region: drag;
}
button {
-webkit-app-region: no-drag;
app-region: no-drag;
}
La dernière étape consiste à rendre l'application réactive aux modifications de la barre de titre. Dans une véritable approche d'amélioration progressive, je ne charge le code de cette fonctionnalité que lorsque le navigateur la prend en charge.
if ('windowControlsOverlay' in navigator) {
import('/wco.js');
}
Chaque fois que la géométrie de superposition des commandes de fenêtre change, je modifie l'application pour qu'elle ait l'air aussi naturelle que possible. Il est conseillé de débouncer cet événement, car il peut être déclenché fréquemment lorsque l'utilisateur redimensionne la fenêtre. Plus précisément, j'applique la classe wco
à certains éléments, ce qui déclenche mon CSS ci-dessus, et je modifie également la couleur du thème. Je peux détecter si le calque des commandes de fenêtre est visible en vérifiant la propriété 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();
Maintenant que tout est en place, j'obtiens un widget de calculatrice qui ressemble presque au classique Winamp avec l'un des anciens thèmes Winamp. Je peux désormais placer librement la calculatrice sur mon bureau et activer la fonctionnalité de contrôle des fenêtres en cliquant sur le chevron en haut à droite.
Une cellule solaire qui fonctionne réellement
Pour un geek ultime, j'avais bien sûr besoin de faire fonctionner la cellule solaire. La calculatrice ne doit fonctionner que s'il y a suffisamment de lumière. Pour ce faire, j'ai défini le opacity
CSS des chiffres sur l'écran via une variable CSS --opacity
que je contrôle via JavaScript.
:root {
--opacity: 0.75;
}
#calc_expression,
#calc_result {
opacity: var(--opacity);
}
Pour détecter si la luminosité est suffisante pour que la calculatrice fonctionne, j'utilise l'API AmbientLightSensor
. Pour que cette API soit disponible, j'ai dû définir l'indicateur #enable-generic-sensor-extra-classes
dans about:flags
et demander l'autorisation 'ambient-light-sensor'
. Comme précédemment, j'utilise l'amélioration progressive pour ne charger le code pertinent que lorsque l'API est compatible.
if ('AmbientLightSensor' in window) {
import('/als.js');
}
Le capteur renvoie la lumière ambiante en unités lux chaque fois qu'une nouvelle lecture est disponible. En me basant sur un tableau de valeurs des situations de luminosité typiques, j'ai trouvé une formule très simple pour convertir la valeur lux en une valeur comprise entre 0 et 1 que j'attribue par programmation à la variable --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();
}
})();
Dans la vidéo ci-dessous, vous pouvez voir comment la calculatrice se met à fonctionner une fois que j'ai allumé suffisamment la lumière de la pièce. Et voilà : une calculatrice solaire skeuomorphique qui fonctionne vraiment. Ma bonne vieille TI-30X SOLAR, qui a fait ses preuves, a vraiment parcouru du chemin.
Démo
N'hésitez pas à tester la démonstration de la calculatrice Designcember et à consulter le code source sur GitHub. (Pour installer l'application, vous devez l'ouvrir dans sa propre fenêtre. La version intégrée ci-dessous ne déclenchera pas la mini-info-barre.)
Joyeux Designcember !