利用 Window Controls Overlay API 和 Ambient Light Sensor API 在网络上重新创建太阳能计算器的拟态尝试。
挑战
我是 20 世纪 80 年代的孩子。上高中时,太阳能计算器是一度盛行的事物。我们都获得了学校提供的 TI-30X 太阳能 (TI-30X SOLAR),并且我还记得我们通过计算 69 的阶乘(TI-30X 可以处理的最高数字)来对照基准计算我们的计算器。(速度差异非常可衡量,我仍不清楚原因)。
近 28 年过去了,我想在 Designcember 中,用 HTML、CSS 和 JavaScript 重新创建该计算器将是一项有趣的挑战。我不是很了解设计师,也没有从头开始,而是用了 Sassja Ceballos 的 CodePen。
使其可安装
尽管开局还不错,但我决定加大力度,展现完全的拟态魅力。第一步是将其设为 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');
}
})(),
);
});
与移动平台融为一体
既然应用现在是可以安装的,下一步就是使其尽可能与操作系统应用融为一体。在移动设备上,我可以在 Web 应用清单中将显示模式设置为 fullscreen
,从而完成此操作。
{
"display": "fullscreen"
}
在有摄像头孔或缺口的设备上,请调整视口,使内容覆盖整个屏幕,让应用看起来非常华丽。
<meta name="viewport" content="initial-scale=1, viewport-fit=cover" />
与桌面设备完美融合
在桌面设备上,我可以使用一项很酷的功能:窗口控件叠加层,它允许我在应用窗口的标题栏中显示内容。第一步是替换显示模式回退序列,使其在 window-controls-overlay
可用时尝试使用它。
{
"display_override": ["window-controls-overlay"]
}
这样会使标题栏有效地消失,而内容会向上移动到标题栏区域,就像标题栏不存在一样。我的想法是将拟态太阳能电池向上移动到标题栏中,并将计算器界面的其余部分向下移动,这可以通过一些使用 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);
}
接下来,我需要决定将哪些元素设为可拖动,因为我通常用于拖动的标题栏不可用。在经典 widget 的样式中,我甚至可以通过应用 (-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();
现在,一切就绪后,我得到了一个计算器 widget,它与经典的 Winamp 与一个老派的 Winamp 主题类似。现在,我可以将计算器随意地放在桌面上,并点击右上角的 V 形图标激活窗口控件功能。
真正能工作的太阳能电池
出于终极极客的需要,我当然需要让太阳能电池真正发挥作用。只有在光线充足时,计算器才能正常运行。我的建模方法是通过一个 CSS 变量 --opacity
(我通过 JavaScript 控制)设置屏幕上显示数字的 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 为单位返回环境光。根据典型光照情况的值表,我提出了一个非常简单的公式,用于将勒克斯值转换为 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 太阳能发电技术确实取得了长足的进步。
演示
请务必玩转 Designcember 计算器演示,并查看 Glitch 上的源代码。(如需安装应用,您需要在应用自己的窗口中打开它。下面的嵌入式版本不会触发迷你信息栏。)
设计人员快乐!