尝试使用 Window Controls Overlay API 和 Ambient Light Sensor API 在 Web 上重新创建太阳能计算器的拟物化界面。
挑战
我是 80 年代的孩子。我上高中时,太阳能计算器很流行。学校给我们每人发了一台 TI-30X SOLAR,我至今仍记得我们计算 69 的阶乘(这是 TI-30X 能处理的最大数字)时,彼此对比计算器性能的愉快时光。(速度差异非常明显,但我仍不知道原因。)
现在,距离那次经历已经过去了将近 28 年,我认为在 HTML、CSS 和 JavaScript 中重新创建计算器会是一项有趣的 Designcember 挑战。我不是设计师,因此并未从头开始,而是使用了 Sassja Ceballos 的 CodePen。
使其可安装
虽然这是一个不错的开端,但我决定再加把劲,让它成为一款出色的拟物化应用。第一步是将其转换为 PWA,以便用户可以安装。我在 Glitch 上维护一个基准 PWA 模板,每当需要快速演示时,我都会对其进行混剪。该服务工作器不会为您赢得任何编码奖项,而且绝对不适合正式版,但足以触发 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" />
与桌面设备融为一体
在桌面设备上,我可以使用一项很酷的功能:窗口控件叠加层,它允许我在应用窗口的标题栏中放置内容。第一步是替换显示模式回退序列,以便在 window-controls-overlay
可用时先尝试使用 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: no-drag
,因此无法用于拖动)之外,我甚至可以通过应用 (-webkit-)app-region: 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 主题。现在,我可以将计算器自由放置在桌面上,并通过点击右上角的箭头来激活窗口控制功能。
实际工作的太阳能电池
当然,为了达到极客的效果,我需要让太阳能电池真正发挥作用。只有在光线充足的情况下,计算器才会正常运行。我通过以下方式对此进行了建模:通过 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 为单位)。根据典型光照情况的值表,我设计了一个非常简单的公式,用于将勒克斯值转换为 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 计算器演示,并查看 Glitch 上的源代码。(如需安装该应用,您需要在其自己的窗口中打开该应用。以下嵌入版本不会触发迷你信息栏。)
Designcember 快乐!