ความพยายามในการสร้างเครื่องคิดเลขพลังงานแสงอาทิตย์ขึ้นมาใหม่บนเว็บด้วย Window Controls Overlay API และ Ambient Light Sensor API
ความท้าทาย
ฉันเป็นเด็กยุค 80 สิ่งที่ได้รับความนิยมอย่างมากตอนที่ฉันเรียนอยู่โรงเรียนมัธยมปลายคือเครื่องคิดเลขพลังงานแสงอาทิตย์ เราทุกคนได้รับเครื่องคิดเลข TI-30X SOLAR จากโรงเรียน และฉันก็มีความทรงจำดีๆ ที่เราเปรียบเทียบเครื่องคิดเลขของเราด้วยการคำนวณแฟกทอเรียลของ 69 ซึ่งเป็นตัวเลขสูงสุดที่ TI-30X สามารถจัดการได้ (ความเร็วที่แตกต่างกันนั้นวัดได้ชัดเจนมาก แต่ฉันก็ยังไม่รู้ว่าทำไม)
ตอนนี้ ผ่านมาเกือบ 28 ปีแล้ว ผมคิดว่าน่าจะเป็นความท้าทายที่สนุกใน Designcember ที่จะสร้างเครื่องคิดเลขขึ้นมาใหม่ใน HTML, CSS และ JavaScript เนื่องจากไม่ได้เป็นนักออกแบบมากนัก ฉันจึงไม่ได้เริ่มตั้งแต่ต้น แต่เริ่มด้วย CodePen โดย Sassja Ceballos

ทำให้ติดตั้งได้
แม้ว่าจะเป็นจุดเริ่มต้นที่ดี แต่ฉันก็ตัดสินใจที่จะเพิ่มความสวยงามแบบ Skeuomorphic ให้เต็มที่ ขั้นตอนแรกคือการทำให้เป็น PWA เพื่อให้ติดตั้งได้
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 ในไฟล์ Manifest ของเว็บแอป
{
"display": "fullscreen"
}
ในอุปกรณ์ที่มีรูหรือรอยบากของกล้อง การปรับแต่งวิวพอร์ตเพื่อให้เนื้อหาครอบคลุมทั้งหน้าจอจะทำให้แอปดูสวยงาม
<meta name="viewport" content="initial-scale=1, viewport-fit=cover" />

การผสมผสานกับเดสก์ท็อป
ในเดสก์ท็อป มีฟีเจอร์เจ๋งๆ ที่ฉันใช้ได้ นั่นคือ Window Controls Overlay ซึ่งช่วยให้ฉันใส่เนื้อหาในแถบชื่อของหน้าต่างแอปได้ ขั้นตอนแรกคือการลบล้างลำดับโหมดการแสดงผลสำรองเพื่อให้พยายามใช้ window-controls-overlay ก่อนเมื่อพร้อมใช้งาน
{
"display_override": ["window-controls-overlay"]
}
ซึ่งจะทำให้แถบชื่อหายไปอย่างมีประสิทธิภาพ และเนื้อหาจะเลื่อนขึ้นไปยังพื้นที่แถบชื่อราวกับว่าไม่มีแถบชื่ออยู่ ไอเดียของฉันคือการย้ายเซลล์แสงอาทิตย์แบบเสมือนจริงขึ้นไปไว้ในแถบชื่อ และย้าย UI เครื่องคิดเลขที่เหลือลงมาตามลำดับ ซึ่งฉันสามารถทำได้ด้วย CSS บางรายการที่ใช้ตัวแปรสภาพแวดล้อม titlebar-area-* คุณจะสังเกตเห็นว่าตัวเลือกทั้งหมดมีคลาส wco ซึ่งจะเกี่ยวข้องกับย่อหน้า 2-3 ย่อหน้าด้านล่าง
#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 แบบเก่า ตอนนี้ฉันสามารถวางเครื่องคิดเลขบนเดสก์ท็อปได้อย่างอิสระและเปิดใช้งานฟีเจอร์การควบคุมหน้าต่างโดยคลิกเครื่องหมายบั้งที่มุมขวาบน

โซลาร์เซลล์ที่ใช้งานได้จริง
แน่นอนว่าเพื่อให้สมกับเป็นเนิร์ดตัวจริง ผมต้องทำให้เซลล์แสงอาทิตย์ทำงานได้จริง เครื่องคิดเลขควรทำงานเมื่อมีแสงสว่างเพียงพอเท่านั้น วิธีที่ฉันจำลองสิ่งนี้คือการตั้งค่า CSS opacity ของตัวเลขบนจอแสดงผลผ่านตัวแปร CSS --opacity ที่ฉันควบคุมผ่าน JavaScript
:root {
--opacity: 0.75;
}
#calc_expression,
#calc_result {
opacity: var(--opacity);
}
หากต้องการตรวจจับว่ามีแสงเพียงพอให้เครื่องคิดเลขทำงานหรือไม่ ฉันจะใช้ API AmbientLightSensor เพื่อให้ API นี้พร้อมใช้งาน ฉันต้องตั้งค่าแฟล็ก #enable-generic-sensor-extra-classes ใน about:flags และขอสิทธิ์ '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 และดูซอร์สโค้ดใน GitHub (หากต้องการติดตั้งแอป คุณต้องเปิดแอปในหน้าต่างของแอปเอง เวอร์ชันที่ฝังด้านล่างจะไม่เรียกใช้แถบข้อมูลขนาดเล็ก)
สุขสันต์เดือน Designcember