Kalkulator projektanta

Skeuomorficzna próba odtworzenia kalkulatora słonecznego w internecie z interfejsami Window Controls Overlay API i Ambient Light Sensor API.

Wyzwanie

Jestem dzieckiem lat 80. W szkole podstawowej używały kalkulatorów słonecznych. Wszyscy dostaliśmy od szkoły SOLAR TI-30X i pamiętam, jak często porównujeliśmy nasze kalkulatory ze sobą, obliczając silnię 69, czyli najwyższą liczbę, jaką mógł obsłużyć TI-30X. (Wariancja szybkości była bardzo mierzona i nadal nie wiem, dlaczego).

Teraz, po niemal 28 latach, pomyślałem, że odtworzenie kalkulatora w HTML, CSS i JavaScript będzie ciekawym wyzwaniem. Jako projektantka nie zaczęłam od zera, ale pracowałam przy użyciu programu CodePen autorstwa Sassji Ceballos.

Widok CodePen ze stosami po lewej stronie paneli HTML, CSS i JS, a po prawej – podgląd kalkulatora.

Pozwól na instalację

Początkowo nie był zły, ale mimo to postanowiłem wzbogacić tę grę o pełen skeuomorficzny charakter. Pierwszym krokiem było utworzenie PWA i możliwość jej zainstalowania. Utrzymuję podstawowy szablon PWA w Glitch, który remiksuję za każdym razem, gdy potrzebuję szybkiej demonstracji. Skrypt service worker nie przyzna Ci żadnej nagrody w zakresie kodowania. Z pewnością nie jest gotowy do produkcji, ale wystarczy, by uruchomić minipasek informacyjny Chromium i zainstalować aplikację.

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');
      }
    })(),
  );
});

Łączenie z urządzeniami mobilnymi

Teraz, gdy aplikację można zainstalować, trzeba jak najbardziej ją zintegrować z aplikacjami systemu operacyjnego. Na urządzeniu mobilnym mogę to zrobić, ustawiając tryb wyświetlania na fullscreen w pliku manifestu aplikacji internetowej.

{
  "display": "fullscreen"
}

W przypadku urządzeń z otworem w aparacie lub wycięciem w telefonie aplikacja może wyglądać atrakcyjniej dzięki zmianie widocznego obszaru, tak aby treść zakrywała cały ekran.

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

Kalkulator projektanta aktywny na pełnym ekranie na telefonie Pixel 6 Pro.

Łączenie z komputerem

Na komputerze mogę użyć ciekawej funkcji: Nakładka z elementami sterującymi okna, która umożliwia umieszczanie treści na pasku tytułu okna aplikacji. Pierwszym krokiem jest zastąpienie sekwencji kreacji zastępczej w trybie wyświetlania, aby przeglądarka używała funkcji window-controls-overlay, gdy jest dostępna.

{
  "display_override": ["window-controls-overlay"]
}

Dzięki temu pasek tytułu całkowicie znika, a treść przesuwa się w górę do obszaru paska tytułu. Chciałbym przenieść skeuomorficzną ogniwo słoneczne w górę na pasek tytułu i w dół interfejsu kalkulatora. Mogę to zrobić za pomocą kodu CSS korzystającego ze zmiennych środowiskowych titlebar-area-*. Zwróć uwagę, że wszystkie selektory zawierają klasę wco, która będzie odpowiednia jeszcze kilka akapitów niżej.

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

Teraz muszę zdecydować, które elementy mają być przeciągane, ponieważ pasek tytułu, którego zwykle używam do przeciągania, nie jest dostępny. W stylu klasycznego widżetu mogę nawet umożliwić przeciąganie całego kalkulatora przez zastosowanie (-webkit-)app-region: drag – oprócz przycisków, które generują (-webkit-)app-region: no-drag, więc nie można ich używać do przeciągania.

#calc_inside.wco,
#calc_solar_cell.wco {
  -webkit-app-region: drag;
  app-region: drag;
}

button {
  -webkit-app-region: no-drag;
  app-region: no-drag;
}

Ostatnim krokiem jest reagowanie aplikacji na zmiany w nakładkach elementów sterujących oknami. Dzięki stopniowemu udoskonalaniu kodu wczytuję kod tej funkcji tylko wtedy, gdy przeglądarka ją obsługuje.

if ('windowControlsOverlay' in navigator) {
  import('/wco.js');
}

Za każdym razem, gdy okno steruje zmianą geometrii nakładki, modyfikuję aplikację, aby wyglądała jak naturalnie. Warto anulować to zdarzenie, ponieważ może ono być wywoływane często, gdy użytkownik zmieni rozmiar okna. Do niektórych elementów stosuję klasę wco, więc włącza się mój CSS z podanego wyżej kodu i zmieniam też kolor motywu. Mogę sprawdzić, czy nakładka z elementami sterującymi okna jest widoczna, sprawdzając właściwość 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();

Po wykonaniu tych wszystkich czynności widzę widżet kalkulatora, który wygląda prawie jak klasyczny Winamp z jednym z motywów starej wersji Winamp. Teraz mogę swobodnie umieścić kalkulator na pulpicie i aktywować elementy sterujące oknami, klikając ikonę w prawym górnym rogu.

Kalkulator projektowy aktywny w trybie samodzielnym z aktywną funkcją Nakładka elementów sterujących oknami. Na ekranie widnieje napis „Google” w alfabecie kalkulatora.

Prawidłowo działające ogniwo słoneczne

Aby osiągnąć ten niezwykłe pasje, musiałem oczywiście sprawić, że ogniwa słoneczne faktycznie działają. Kalkulator powinien działać tylko wtedy, gdy jest wystarczająco dużo światła. Do modelowania tego użyto ustawienia opacity CSS cyfr na wyświetlaczu za pomocą zmiennej CSS --opacity, którą steruję za pomocą JavaScriptu.

:root {
  --opacity: 0.75;
}

#calc_expression,
#calc_result {
  opacity: var(--opacity);
}

Aby sprawdzić, czy ilość światła jest wystarczająca do działania kalkulatora, używam interfejsu API AmbientLightSensor. Aby ten interfejs API był dostępny, muszę ustawić flagę #enable-generic-sensor-extra-classes w about:flags i poprosić o uprawnienie 'ambient-light-sensor'. Tak jak wcześniej, używam progresywnego ulepszania, by wczytywać kod tylko wtedy, gdy interfejs API jest obsługiwany.

if ('AmbientLightSensor' in window) {
  import('/als.js');
}

Czujnik zwraca światło otoczenia w jednostkach lux, gdy dostępny jest nowy odczyt. Na podstawie tabeli wartości typowych sytuacji świetlnych stworzyłem bardzo prostą formułę, która pozwoli przekonwertować wartość w luksach na wartość z zakresu od 0 do 1, którą automatycznie przypisuję do zmiennej --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();
  }
})();

Na filmie poniżej widać, jak kalkulator zacznie działać, gdy odpowiednio oświetlę salę. I o to chodzi: skeuomorficzny kalkulator słoneczny, który faktycznie działa. Moja stara, przetestowana SOLAR TI-30X bardzo się zmieniła.

Pokaz

Wypróbuj prezentację Designcember Calculator i sprawdź kod źródłowy w Glitchu. (Aby zainstalować aplikację, musisz otworzyć ją w osobnym oknie. Umieszczona poniżej wersja nie wyświetla paska informacyjnego).

Szczęśliwego projektowania!