Un tentativo scheumorfico di ricreare una calcolatrice solare sul web con l'API Window Controls Overlay e l'API sensore della luce ambientale.
La sfida
Sono unə bambinə degli anni'80. Quando andavo alle scuole superiori, le calcolatrici solari erano molto di moda. La scuola ci aveva dato una TI-30X SOLAR e ho dei bei ricordi di noi che confrontavamo le nostre calcolatrici calcolando il fattoriale di 69, il numero più alto che la TI-30X poteva gestire. (La variazione di velocità era molto misurabile, non ho ancora idea del perché).
Ora, quasi 28 anni dopo, ho pensato che sarebbe stata una divertente sfida di Designcember ricreare la calcolatrice in HTML, CSS e JavaScript. Non essendo un designer, non ho iniziato da zero, ma con un CodePen di Sassja Ceballos.

Rendila installabile
Non è un cattivo inizio, ma ho deciso di migliorarlo per ottenere un'esperienza scheumorfica completa. Il primo passaggio è stato trasformarla in una PWA in modo che potesse essere installata.
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');
}
})(),
);
});
Integrazione con i dispositivi mobili
Ora che l'app è installabile, il passaggio successivo consiste nel farla integrare il più possibile con le app del sistema operativo. Su
dispositivo mobile, posso farlo impostando la modalità di visualizzazione su fullscreen nel file manifest dell'app web.
{
"display": "fullscreen"
}
Sui dispositivi con un foro o un notch per la fotocamera, modificare l'area visibile in modo che i contenuti coprano l'intero schermo rende l'app fantastica.
<meta name="viewport" content="initial-scale=1, viewport-fit=cover" />

Integrazione con il desktop
Su computer, posso utilizzare una funzionalità interessante:
Window Controls Overlay, che mi consente di inserire contenuti nella barra del titolo
della finestra dell'app. Il primo passaggio consiste nell'eseguire l'override della sequenza di fallback della modalità di visualizzazione in modo che provi a utilizzare prima window-controls-overlay, se disponibile.
{
"display_override": ["window-controls-overlay"]
}
In questo modo, la barra del titolo scompare e i contenuti si spostano verso l'alto nell'area della barra del titolo come se questa non esistesse. La mia idea è spostare la cella solare scheumorfica nella barra del titolo e il resto della UI della calcolatrice verso il basso di conseguenza, cosa che posso fare con un po' di CSS che utilizza le variabili di ambiente titlebar-area-*. Noterai che tutti i selettori hanno una classe wco, che sarà pertinente un paio di paragrafi più avanti.
#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);
}
Ora devo decidere quali elementi rendere trascinabili, poiché la barra del titolo che di solito utilizzo per il trascinamento non è disponibile. Nello stile di un widget classico, posso persino rendere l'intera calcolatrice trascinabile applicando (-webkit-)app-region: drag, ad eccezione dei pulsanti, che ricevono (-webkit-)app-region: no-drag in modo che non possano essere utilizzati per il trascinamento.
#calc_inside.wco,
#calc_solar_cell.wco {
-webkit-app-region: drag;
app-region: drag;
}
button {
-webkit-app-region: no-drag;
app-region: no-drag;
}
Il passaggio finale consiste nel rendere l'app reattiva alle modifiche dell'overlay dei controlli della finestra. In un vero approccio di potenziamento progressivo, carico il codice per questa funzionalità solo quando il browser la supporta.
if ('windowControlsOverlay' in navigator) {
import('/wco.js');
}
Ogni volta che la geometria dell'overlay dei controlli della finestra cambia, modifico l'app per renderla il più naturale possibile. È consigliabile eseguire il debounce di questo evento, poiché può essere attivato di frequente quando l'utente ridimensiona la finestra. In particolare, applico la classe wco ad alcuni elementi, in modo che il mio CSS di cui sopra entri in azione, e cambio anche il colore del tema. Posso rilevare se l'overlay dei controlli della finestra è visibile controllando la proprietà 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();
Ora, con tutto questo, ho un widget calcolatrice che sembra quasi il classico Winamp con uno dei temi Winamp della vecchia scuola. Ora posso posizionare liberamente la calcolatrice sul mio desktop e attivare la funzionalità dei controlli della finestra facendo clic sul simbolo di spunta nell'angolo in alto a destra.

Una cella solare effettivamente funzionante
Per il massimo del geek, ovviamente dovevo far funzionare davvero la cella solare. La calcolatrice dovrebbe funzionare solo se c'è abbastanza luce. Il modo in cui ho modellato questo è impostando la proprietà CSS opacity delle cifre sul display tramite una variabile CSS --opacity che controllo tramite JavaScript.
:root {
--opacity: 0.75;
}
#calc_expression,
#calc_result {
opacity: var(--opacity);
}
Per rilevare se è disponibile luce sufficiente per il funzionamento della calcolatrice, utilizzo l'API AmbientLightSensor. Affinché questa API sia disponibile, ho dovuto impostare il flag #enable-generic-sensor-extra-classes in about:flags e richiedere l'autorizzazione 'ambient-light-sensor'. Come prima, utilizzo il potenziamento progressivo per caricare il codice pertinente solo quando l'API è supportata.
if ('AmbientLightSensor' in window) {
import('/als.js');
}
Il sensore restituisce la luce ambientale in unità lux ogni volta che è disponibile una nuova lettura. In base a una tabella di valori di situazioni di luce tipiche, ho ideato una formula molto semplice per convertire il valore di lux in un valore compreso tra 0 e 1 che assegno in modo programmatico alla variabile --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();
}
})();
Nel video qui sotto puoi vedere come la calcolatrice inizia a funzionare quando accendo la luce della stanza con un'intensità sufficiente. Ecco fatto: una calcolatrice solare scheumorfica che funziona davvero. La mia vecchia e collaudata TI-30X SOLAR ha fatto molta strada.
Demo
Assicurati di provare la demo della calcolatrice di Designcember e dai un'occhiata al codice sorgente su GitHub. Per installare l'app, devi aprirla nella sua finestra. La versione incorporata di seguito non attiverà la barra informativa ridotta).
Buon Designcember!