Ein skeuomorpher Versuch, einen Sonnenrechner im Web mit der Window Controls Overlay API und der Ambient Light Sensor API nachzubilden.
Die Herausforderung
Ich bin ein Kind der 1980er-Jahre. Als ich zur Schule ging, waren Solarrechner der letzte Schrei. Die Schule stellte uns allen einen TI-30X SOLAR zur Verfügung. Ich erinnere mich gern daran, wie wir unsere Taschenrechner miteinander verglichen haben, indem wir das Fakultätenpotenz von 69 berechneten, die höchste Zahl, die der TI-30X verarbeiten konnte. (Die Geschwindigkeitsschwankungen waren sehr messbar, ich weiß aber immer noch nicht, warum.)
Jetzt, fast 28 Jahre später, dachte ich, es wäre eine lustige Designcember-Herausforderung, den Rechner in HTML, CSS und JavaScript neu zu erstellen. Da ich kein Designer bin, habe ich nicht bei null angefangen, sondern mit einem CodePen von Sassja Ceballos.
Installation ermöglichen
Das war zwar kein schlechter Anfang, aber ich habe mich entschieden, das Ganze noch skeuomorphischer zu gestalten. Der erste Schritt bestand darin, die Website in eine PWA umzuwandeln, damit sie installiert werden konnte. Ich pflege eine Grundlegende PWA-Vorlage auf Glitch, die ich remixe, wenn ich eine schnelle Demo brauche. Der Service Worker wird Ihnen keinen Programmierpreis einbringen und ist definitiv nicht für die Produktion bereit. Er reicht jedoch aus, um die Mini-Infobar von Chromium auszulösen, damit die App installiert werden kann.
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');
}
})(),
);
});
Mit Mobilgeräten verschmelzen
Nachdem die App installiert werden kann, besteht der nächste Schritt darin, sie so weit wie möglich in die Betriebssystem-Apps einzubinden. Auf Mobilgeräten kann ich das tun, indem ich im Web-App-Manifest den Anzeigemodus auf fullscreen
setze.
{
"display": "fullscreen"
}
Auf Geräten mit Kameraloch oder Notch können Sie den Viewport so anpassen, dass die Inhalte das gesamte Display ausfüllen.
<meta name="viewport" content="initial-scale=1, viewport-fit=cover" />
Mit dem Desktop verschmelzen
Auf dem Computer gibt es eine coole Funktion, die ich verwenden kann: das Einblenden von Fenstersteuerelementen. Damit kann ich Inhalte in die Titelleiste des App-Fensters einfügen. Im ersten Schritt überschreiben Sie die Fallback-Sequenz für den Anzeigemodus, damit zuerst window-controls-overlay
verwendet wird, wenn es verfügbar ist.
{
"display_override": ["window-controls-overlay"]
}
Dadurch wird die Titelleiste effektiv ausgeblendet und der Inhalt wird in den Bereich der Titelleiste verschoben, als wäre die Titelleiste nicht vorhanden. Ich möchte die skeuomorphe Solarzelle in die Titelleiste verschieben und den Rest der Benutzeroberfläche des Rechners entsprechend nach unten. Das kann ich mit CSS tun, das die Umgebungsvariablen titlebar-area-*
verwendet. Sie werden feststellen, dass alle Auswahlschaltflächen eine wco
-Klasse haben, die einige Absätze weiter unten relevant wird.
#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);
}
Als Nächstes muss ich entscheiden, welche Elemente ich verschiebbar machen möchte, da die Titelleiste, die ich normalerweise zum Ziehen verwenden würde, nicht verfügbar ist. Im Stil eines klassischen Widgets kann ich sogar den gesamten Rechner per Drag-and-drop verschieben, indem ich (-webkit-)app-region: drag
anwende, mit Ausnahme der Schaltflächen, die (-webkit-)app-region: no-drag
erhalten, damit sie nicht verschoben werden können.
#calc_inside.wco,
#calc_solar_cell.wco {
-webkit-app-region: drag;
app-region: drag;
}
button {
-webkit-app-region: no-drag;
app-region: no-drag;
}
Im letzten Schritt sorgen Sie dafür, dass die App auf Änderungen am Overlay der Fenstersteuerung reagiert. Bei einer echten progressiven Verbesserung wird der Code für diese Funktion nur geladen, wenn der Browser sie unterstützt.
if ('windowControlsOverlay' in navigator) {
import('/wco.js');
}
Jedes Mal, wenn sich die Geometrie des Fensterkontroll-Overlays ändert, passe ich die App so an, dass sie so natürlich wie möglich aussieht. Es empfiehlt sich, dieses Ereignis zu dämpfen, da es häufig ausgelöst werden kann, wenn der Nutzer die Größe des Fensters ändert. Ich wende die Klasse wco
auf einige Elemente an, damit mein CSS-Code oben angewendet wird. Außerdem ändere ich die Designfarbe. Ich kann anhand der Eigenschaft navigator.windowControlsOverlay.visible
erkennen, ob das Overlay für die Fenstersteuerung sichtbar ist.
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();
Jetzt habe ich ein Rechenmaschinen-Widget, das fast wie das klassische Winamp mit einem der alten Winamp-Designs aussieht. Ich kann den Rechner jetzt frei auf dem Desktop platzieren und die Fenstersteuerung aktivieren, indem ich oben rechts auf den Dreipunkt-Pfeil klicke.
Eine tatsächlich funktionierende Solarzelle
Für den ultimativen Nerd-Faktor musste die Solarzelle natürlich auch funktionieren. Der Rechner sollte nur bei ausreichender Beleuchtung funktionieren. Ich habe das so modelliert, dass ich das CSS opacity
der Ziffern auf dem Display über eine CSS-Variable --opacity
festlege, die ich über JavaScript steuere.
:root {
--opacity: 0.75;
}
#calc_expression,
#calc_result {
opacity: var(--opacity);
}
Um zu erkennen, ob genügend Licht für die Funktion des Rechners vorhanden ist, verwende ich die AmbientLightSensor
API. Damit diese API verfügbar ist, musste ich das Flag #enable-generic-sensor-extra-classes
in about:flags
festlegen und die Berechtigung 'ambient-light-sensor'
anfordern. Wie zuvor verwende ich die progressive Verbesserung, um nur den relevanten Code zu laden, wenn die API unterstützt wird.
if ('AmbientLightSensor' in window) {
import('/als.js');
}
Der Sensor gibt das Umgebungslicht in Lux zurück, sobald ein neuer Messwert verfügbar ist. Anhand einer Tabelle mit Werten für typische Lichtsituationen habe ich eine sehr einfache Formel entwickelt, um den Lux-Wert in einen Wert zwischen 0 und 1 umzuwandeln, den ich der Variablen --opacity
programmatisch zuweise.
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();
}
})();
Im Video unten sehen Sie, wie der Rechner funktioniert, sobald ich das Raumlicht ausreichend hell einschalte. Und das ist es: ein skeuomorpher Solarrechner, der tatsächlich funktioniert. Mein guter alter, bewährter TI-30X SOLAR hat es in der Tat weit gebracht.
Demo
Sehen Sie sich die Designcember-Rechner-Demo und den Quellcode auf Glitch an. (Zum Installieren der App müssen Sie sie in einem eigenen Fenster öffnen. In der eingebetteten Version unten wird die Mini-Infobar nicht ausgelöst.)
Viel Spaß beim Designcember!