設計工具計算機

試圖使用 Window Controls Overlay API 和 Ambient Light Sensor API 在網路上重新建立太陽能計算器。

難題

我是 1980 年代的孩子。而我讀高中時遇到的爆紅事物,就是太陽能計算機。學校都有一個 TI-30X SOLAR,而我也因此深受其擾,我們計算 69 的因數 (TI-30X 所能處理的最高值),藉此對計算機進行基準測試。(速度偏差非常可評估,我還是不知道原因)。

現在,將近 28 年後,我認為在 HTML、CSS 和 JavaScript 中重建計算機會是非常有趣的設計挑戰。我不是設計人員,沒有從頭開始設計,而是使用 Sassja CeballosCodePen

CodePen 檢視畫面,左側顯示堆疊的 HTML、CSS 和 JS 面板,右側是計算機預覽。

設為可安裝

雖然一開始還不賴,但我還是決定投入資源,享受完整的偏見。第一步是將其設為 PWA,以便順利安裝。我會用 Glitch 上的基準 PWA 範本,快速示範,隨時都能製作重混作品。這項服務的 Service Worker 不會贏得任何程式設計獎,而且無法在實際工作環境中使用,但足以觸發 Chromium 迷你資訊列,以便安裝該應用程式。

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

結合行動裝置

現在可以安裝應用程式了,下一步是盡可能融入作業系統的應用程式。使用行動裝置時,我可以在網頁應用程式資訊清單中,將顯示模式設為 fullscreen

{
  "display": "fullscreen"
}

對於相機有洞或凹口的裝置,請調整可視區域,讓內容覆蓋整個螢幕,讓應用程式看起來更美觀。

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

在 Pixel 6 Pro 手機上執行全螢幕設計的設計設計師計算機。

與電腦版混合

在電腦上,有一個很酷的功能可以使用:視窗控制項重疊,這可讓我將內容放在應用程式視窗的標題列。第一步是覆寫顯示模式備用序列,讓系統在可用時先嘗試使用 window-controls-overlay

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

這樣可以有效移除標題列,並將內容移至標題列區域,如同標題列不存在一樣。我的想法是將結構體太陽能電池向上移至標題列,然後向下移動計算機 UI 的其餘部分,以便使用一些使用 titlebar-area-* 環境變數的 CSS。您會發現所有選取器都具有 wco 類別,該類別與幾個段落下方相關。

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

接下來,我需要決定哪些元素可以拖曳,因為我通常用於拖曳的標題列無法使用。在傳統小工具的樣式中,我甚至可以套用 (-webkit-)app-region: drag (除了按鈕之外,取得 (-webkit-)app-region: no-drag,使其無法拖曳),將整個計算機設為可拖曳。

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

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

最後一個步驟,是讓應用程式回應視窗控制項變更。在真正的漸進式強化方法中,我只有在瀏覽器支援這項功能時,才會載入這項功能的程式碼。

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

每當視窗控制項重疊的幾何圖形發生變化時,我都會修改應用程式,盡可能讓畫面看起來更自然。建議您去除這個事件,因為使用者調整視窗大小時,可能會經常觸發這個事件。不僅如此,我將 wco 類別套用至部分元素,讓上述的 CSS 開始生效,同時也改變主題顏色。我可以檢查 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();

完成上述所有事項後,我就會收到與經典「Winamp」相似的計算機小工具,內含其中一個老派的「Winamp 主題」。我現在就可以自由將計算機放在桌面上,然後按一下右上角的 V 形圖示來啟用視窗控制功能。

在啟用「視窗控制重疊」功能的情況下,在獨立模式下執行設計器計算機。顯示螢幕拼出了「Google」的計算機字母。

實際運作中的太陽能電池

我當然希望太陽能電池能實際運作,計算機只會在電量充足時正常運作。我建模的方式,是透過透過 JavaScript 控制的 CSS 變數 --opacity,設定顯示畫面中數字的 CSS opacity

:root {
  --opacity: 0.75;
}

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

我使用 AmbientLightSensor API,偵測計算機是否有足夠的光源可用。為了使用這個 API,我需要在 about:flags 中設定 #enable-generic-sensor-extra-classes 旗標,並要求 'ambient-light-sensor' 權限。和先前一樣,我會採用漸進式強化,只在支援 API 時才載入相關程式碼。

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

有新讀數時,感應器會以 lux 單位傳回環境光度。根據一般光源的值表,我打造出了一個簡單的公式,以便將 lux 值轉換為 0 到 1 之間的值,並透過程式輔助方式指派給 --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();
  }
})();

在下方的影片中,您可以看到計算機在房間燈亮一點後開始運作的情形。以上就是一個可以運作的素描太陽計算機。我長久以來的 TI-30X SOLAR 真的有長足的進展。

操作示範

請務必參考 Designcember Calculator 示範,並參考 Glitch 中的原始碼。(如要安裝此應用程式,您需要在另一個視窗中開啟。下方的嵌入版本不會觸發迷你資訊列)。

祝您設計師一切順利!