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. Obliczaliśmy wtedy silnię 69, czyli najwyższą liczbę, jaką potrafił obsłużyć kalkulator TI-30X. (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. Pracownik usługi nie zdobędzie za Ciebie żadnej nagrody za kodowanie i zdecydowanie nie jest gotowy do wdrożenia, ale wystarczy do wywołania minipaska informacyjnego 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');
      }
    })(),
  );
});

Dopasowanie 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 pola widzenia 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.

Łączenie z pulpitem

Na komputerze jest fajna funkcja: nakładka z elementami sterującymi okna, która pozwala umieszczać treści na pasku tytułu 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ą skeuomorfizyczną do paska tytułu, a pozostałą część interfejsu kalkulatora odpowiednio 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 geometria okna sterowania zmienia się, 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 przezroczystość nakładki sterowania oknem jest widoczna, można 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 „Google” jest zapisany w alfabecie kalkulatora.

działające ogniwo słoneczne;

Oczywiście musiałem też sprawić, aby panel słoneczny działał. Kalkulator powinien działać tylko wtedy, gdy jest wystarczająco dużo światła. Aby to zamodelować, ustawiam wartość opacity w CSS cyfr na wyświetlaczu za pomocą zmiennej CSS --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ę do 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 zapoznaj się z kodem źródłowym na Glitch. (Aby zainstalować aplikację, musisz otworzyć ją w osobnym oknie. Poniżej znajduje się wersja do wklejenia, która nie spowoduje wyświetlenia mini informacji.

Wesołego Designcembera!