Calcolatore Designcember

Un tentativo scheuomorfico di ricreare una calcolatrice solare sul web con l'API Window Controls Overlay e l'API Ambient Light Sensor.

La sfida

Sono un ragazzo degli anni'80. Quando andavo al liceo, le calcolatrici solari erano di gran moda. La scuola ci ha dato a tutti una TI-30X SOLAR e ho un bel ricordo di quando 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é.)

Quasi 28 anni dopo, ho pensato che sarebbe stata una sfida divertente per Designcember ricreare la calcolatrice in HTML, CSS e JavaScript. Non essendo un grande designer, non ho iniziato da zero, ma con un CodePen di Sassja Ceballos.

Visualizzazione CodePen con i riquadri HTML, CSS e JS impilati a sinistra e l'anteprima della calcolatrice a destra.

Rendere l'app installabile

Anche se non era un brutto inizio, ho deciso di pomparlo per un'esperienza skeumorfica completa. Il primo passo è stato trasformarlo in una PWA in modo che potesse essere installato.

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 il dispositivo mobile

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 una tacca per la fotocamera, modificare la finestra in modo che i contenuti coprano l'intero schermo rende l'app splendida.

<meta name="viewport" content="initial-scale=1, viewport-fit=cover" />

L&#39;app Calcolatrice di Designcember in esecuzione a schermo intero su uno smartphone Pixel 6 Pro.

Integrazione con il computer

Sul computer, c'è una funzionalità interessante che posso utilizzare: Overlay "window-controls", che mi consente di inserire contenuti nella barra del titolo della finestra dell'app. Il primo passaggio consiste nell'override della sequenza di fallback della modalità di visualizzazione in modo che tenti di utilizzare window-controls-overlay per primo quando è 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 non ci fosse. La mia idea è di spostare la cella solare scheuomorfica nella barra del titolo e il resto dell'interfaccia utente 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 tra qualche paragrafo.

#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);
}

Poi devo decidere quali elementi rendere trascinabili, dato che la barra del titolo che di solito uso per trascinare 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 ottengono (-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 miglioramento 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 della sovrapposizione dei controlli della finestra cambia, modifico l'app per farla apparire 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 CSS riportato 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, ottengo un widget calcolatore che sembra quasi il classico Winamp con uno dei temi Winamp old school. Ora posso posizionare liberamente la calcolatrice sul desktop e attivare la funzionalità di controllo della finestra facendo clic sul chevron nell'angolo in alto a destra.

Calcolatore Designcember in esecuzione in modalità autonoma con la funzionalità Overlay dei controlli della finestra attiva. Sul display viene visualizzata la parola &quot;Google&quot; nell&#39;alfabeto della calcolatrice.

Un pannello solare funzionante

Per il massimo del geekery, ovviamente dovevo far funzionare davvero la cella solare. La calcolatrice dovrebbe funzionare solo se c'è abbastanza luce. Ho modellato questo elemento impostando il 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 del calcolatore, utilizzo l'API AmbientLightSensor. Per rendere disponibile questa API, ho dovuto impostare il flag #enable-generic-sensor-extra-classes in about:flags e richiedere l'autorizzazione 'ambient-light-sensor'. Come prima, utilizzo il miglioramento progressivo per caricare solo il codice pertinente 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 lux in un valore compreso tra 0 e 1 che assegno programmaticamente 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 una volta che la luce della stanza è sufficiente. Ecco fatto: un calcolatore solare scheuomorfico che funziona davvero. La mia vecchia e affidabile TI-30X SOLAR ha fatto molta strada.

Demo

Assicurati di provare la demo del calcolatore di Designcember e di consultare il codice sorgente su GitHub. (Per installare l'app, devi aprirla in una finestra separata. La versione incorporata riportata di seguito non attiverà la mini barra delle informazioni.)

Buon Designcember.