Kalkulator projektanta

Skeumorficzna próba odtworzenia kalkulatora słonecznego w internecie za pomocą interfejsu Window Controls Overlay API i interfejsu Ambient Light Sensor API.

Wyzwanie

Jestem dzieckiem lat 80. Kiedy chodziłem do liceum, modne były kalkulatory słoneczne. Wszyscy otrzymaliśmy od szkoły kalkulator TI-30X SOLAR. Mam miłe wspomnienia z porównywania naszych kalkulatorów, gdy obliczaliśmy silnię 69, czyli najwyższą liczbę, z jaką TI-30X mógł sobie poradzić. (Różnica w szybkości była bardzo duża, nadal nie wiem, dlaczego).

Teraz, prawie 28 lat później, pomyślałem, że fajnym wyzwaniem na Designcember byłoby odtworzyć kalkulator w HTML, CSS i JavaScript. Nie jestem projektantem, więc nie zaczynałam od zera, ale od CodePen autorstwa Sassji Ceballos.

Widok CodePen z ułożonymi w kolorze panelami HTML, CSS i JS po lewej stronie oraz podglądem kalkulatora po prawej stronie

Umożliw instalację

Chociaż nie był to zły początek, postanowiłem pójść na całość i użyć skeumorfizmu. Pierwszym krokiem było stworzenie PWA, aby można było ją zainstalować. Na platformie Glitch mam podstawowy szablon PWA, który remiksuję, gdy potrzebuję szybkiej wersji demonstracyjnej. Ten serwis nie zdobędzie żadnej nagrody za kodowanie i zdecydowanie nie jest gotowy do wdrożenia, ale wystarczy do wywołania minipaska informacji w Chromium, aby można było 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');
      }
    })(),
  );
});

Dopasowywanie do urządzenia mobilnego

Teraz, gdy aplikacja jest gotowa do zainstalowania, musisz ją jak najbardziej dostosować do aplikacji systemu operacyjnego. Na telefonie mogę to zrobić, ustawiając tryb wyświetlania na fullscreen w pliku manifestu aplikacji internetowej.

{
  "display": "fullscreen"
}

Na urządzeniach z otworem na aparat lub wycięciem dostosowanie widoku tak, aby zawartość wypełniała cały ekran, sprawi, że aplikacja będzie wyglądać świetnie.

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

Kalkulator Designcember w trybie pełnoekranowym na telefonie Pixel 6 Pro.

Mieszanie z pulpitem

Na komputerze jest fajna funkcja: nakładka z elementami sterującymi okna, która pozwala mi umieszczać treści na pasku tytułowym okna aplikacji. Pierwszym krokiem jest zastąpienie sekwencji zastępczej trybu wyświetlania, aby najpierw próbować użyć window-controls-overlay, jeśli jest dostępny.

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

W ten sposób pasek tytułu znika, a treści przesuwają się w obszarze tego paska, tak jakby go nie było. Mam pomysł, aby przesunąć komórkę słoneczną skeumorficzną do paska tytułu, a pozostałą część interfejsu kalkulatora w odpowiednim kierunku w dół. Mogę to zrobić za pomocą kodu CSS, który używa zmiennych środowiskowych titlebar-area-*. Zauważ, że wszystkie selektory mają klasę wco, która będzie istotna w kilku kolejnych akapitach.

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

Następnie muszę zdecydować, które elementy mają być przeciągane, ponieważ pasek tytułu, którego zwykle używam do przeciągania, jest niedostępny. W stylu klasycznego widżetu mogę nawet uczynić cały kalkulator przeciąganym, stosując (-webkit-)app-region: drag, z wyjątkiem przycisków, które mają (-webkit-)app-region: no-drag, aby nie można było ich przeciągać.

#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 skonfigurowanie aplikacji tak, aby reagowała na zmiany w nakładce z elementami sterującymi okna. W ramach prawdziwego podejścia do ulepszania stopniowego ładuję kod tej funkcji tylko wtedy, gdy przeglądarka ją obsługuje.

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

Gdy zmienia się geometria nakładki sterowania oknem, modyfikuję aplikację, aby wyglądała jak najbardziej naturalnie. Warto zastosować debouncing tego zdarzenia, ponieważ może ono być często wywoływane, gdy użytkownik zmienia rozmiar okna. Mianowicie, zastosowałem klasę wco do niektórych elementów, aby zadziałał mój kod CSS z powyższego przykładu, a także zmieniłem kolor motywu. Czy overlay sterowania oknem jest widoczne, mogę sprawdzić, 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();

Teraz, gdy wszystko jest gotowe, mam widżet kalkulatora, który przypomina klasyczny Winamp z jedną ze starych motywów Winampa. Teraz mogę swobodnie umieszczać kalkulator na pulpicie i aktywować funkcję sterowania oknem, klikając trójkąt w prawym górnym rogu.

Kalkulator Designcember działający w trybie samodzielnym z aktywną nakładką z elementami sterującymi okna. Wyświetlany napis to „Google” zapisane alfabetem kalkulatora.

działające ogniwo słoneczne;

Oczywiście, aby było to naprawdę geekowskie, musiałem sprawić, by panel słoneczny działał. Kalkulator powinien działać tylko wtedy, gdy jest wystarczająco dużo światła. W tym celu ustawiam wartość opacity w arkuszu CSS za pomocą zmiennej --opacity, którą kontroluję za pomocą JavaScriptu.

:root {
  --opacity: 0.75;
}

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

Aby wykryć, czy jest wystarczająco dużo światła, aby kalkulator mógł działać, używam interfejsu API AmbientLightSensor. Aby ten interfejs API był dostępny, musiałem ustawić flagę #enable-generic-sensor-extra-classesabout:flags i poprosić o uprawnienie 'ambient-light-sensor'. Podobnie jak wcześniej, używam stopniowego ulepszania, aby wczytywać odpowiedni kod tylko wtedy, gdy interfejs API jest obsługiwany.

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

Czujnik zwraca jasność otoczenia w jednostkach lux, gdy tylko pojawią się nowe dane. Na podstawie tabeli wartości typowych sytuacji oświetleniowych udało mi się opracować bardzo prostą formułę, która zamienia wartość luksów na wartość z zakresu od 0 do 1, którą przypisuję zmiennej --opacity za pomocą kodu.

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

W poniższym filmie możesz zobaczyć, jak kalkulator zaczyna działać, gdy włączę światło w pokoju. Oto on: skeumorficzny kalkulator słoneczny, który naprawdę działa. Mój stary, sprawdzony przez lata kalkulator TI-30X SOLAR przeszedł długą drogę.

Prezentacja

Wypróbuj demo kalkulatora Designcember i sprawdź kod źródłowy na Glitch. (Aby zainstalować aplikację, musisz otworzyć ją w osobnym oknie. Umieszczona poniżej wersja nie spowoduje wyświetlenia mini informacji.

Wesołego Designcembera!