เครื่องคิดเลข Designcember Calculator

ความพยายามในการจำลองเครื่องคิดเลขพลังงานแสงอาทิตย์บนเว็บด้วย Window Controls Overlay API และ Ambient Light Sensor API

ความท้าทาย

ฉันเป็นเด็กยุค 80 สิ่งหนึ่งที่ได้รับความนิยมอย่างมากตอนที่ฉันอยู่ชั้นมัธยมปลายคือเครื่องคิดเลขพลังงานแสงอาทิตย์ โรงเรียนให้TI-30X SOLAR กับพวกเราทุกคน และฉันก็มีความทรงจำที่น่าประทับใจเกี่ยวกับการเปรียบเทียบเครื่องคิดเลขของเรากับเครื่องคิดเลขของคนอื่นๆ โดยการคำนวณ แฟกทอเรียลของ 69 ซึ่งเป็นตัวเลขสูงสุดที่ TI-30X สามารถคำนวณได้ (ความเร็วที่แตกต่างกันนั้นวัดได้ชัดเจนมาก แต่ฉันก็ยังไม่รู้ว่าทำไม)

ตอนนี้ผ่านมาเกือบ 28 ปีแล้ว ฉันคิดว่าการสร้างเครื่องคิดเลขขึ้นมาใหม่ใน HTML, CSS และ JavaScript น่าจะเป็นความท้าทายที่สนุกใน Designcember เนื่องจากฉันไม่ได้เป็นนักออกแบบ ฉันจึงไม่ได้เริ่มจากศูนย์ แต่ใช้ CodePen ของ Sassja Ceballos

มุมมอง CodePen ที่มีแผง HTML, CSS และ JS ซ้อนกันทางด้านซ้าย และตัวอย่างเครื่องคิดเลขทางด้านขวา

ทำให้ติดตั้งได้

แม้ว่าจะเป็นการเริ่มต้นที่ไม่เลว แต่ฉันก็ตัดสินใจที่จะเพิ่มความสมจริงให้เต็มที่ ขั้นตอนแรกคือ การเปลี่ยนให้เป็น 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 ใน Web App Manifest

{
  "display": "fullscreen"
}

ในอุปกรณ์ที่มีรูหรือรอยบากสำหรับกล้อง การปรับแต่งวิวพอร์ตเพื่อให้ เนื้อหาครอบคลุมทั้งหน้าจอจะทำให้แอปดูสวยงาม

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

เครื่องคิดเลข Designcember ทำงานแบบเต็มหน้าจอในโทรศัพท์ Pixel 6 Pro

การผสานรวมกับเดสก์ท็อป

บนเดสก์ท็อปมีฟีเจอร์เจ๋งๆ ที่ฉันใช้ได้ นั่นคือการวางซ้อนการควบคุมหน้าต่าง ซึ่งช่วยให้ฉันใส่เนื้อหาในแถบชื่อของหน้าต่างแอปได้ ขั้นตอนแรกคือการลบลำดับการสำรองโหมดการแสดงผลเพื่อลองใช้ window-controls-overlay ก่อนเมื่อพร้อมใช้งาน

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

ซึ่งจะทำให้แถบชื่อหายไปและเนื้อหาจะเลื่อนขึ้นไปในพื้นที่แถบชื่อราวกับว่า ไม่มีแถบชื่อ ไอเดียของฉันคือการย้ายเซลล์แสงอาทิตย์แบบ Skeuomorphic ขึ้นไปที่แถบชื่อ และย้าย 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;
}

ขั้นตอนสุดท้ายคือการทําให้แอปตอบสนองต่อการเปลี่ยนแปลงการวางซ้อนการควบคุมหน้าต่าง ในแนวทาง Progressive Enhancement ที่แท้จริง ฉันจะโหลดโค้ดสำหรับฟีเจอร์นี้เฉพาะเมื่อเบราว์เซอร์รองรับเท่านั้น

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 แบบย้อนยุค ตอนนี้ฉันสามารถ วางเครื่องคิดเลขบนเดสก์ท็อปได้อย่างอิสระและเปิดใช้งานฟีเจอร์ควบคุมหน้าต่างได้โดยคลิก เชฟรอนที่มุมขวาบน

เครื่องคิดเลข Designcember ทำงานในโหมดสแตนด์อโลนโดยเปิดใช้ฟีเจอร์การวางซ้อนการควบคุมหน้าต่าง โดยจอแสดงผลจะสะกดคำว่า &quot;Google&quot; ในตัวอักษรของเครื่องคิดเลข

โซลาร์เซลล์ที่ใช้งานได้จริง

และเพื่อความสมบูรณ์แบบ ผมก็ต้องทำให้เซลล์แสงอาทิตย์ทำงานได้จริง เครื่องคิดเลข ควรทำงานได้ก็ต่อเมื่อมีแสงสว่างเพียงพอเท่านั้น วิธีที่ฉันสร้างโมเดลนี้คือการตั้งค่า 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');
}

เซ็นเซอร์จะแสดงแสงแวดล้อมในหน่วยลักซ์ทุกครั้งที่มีค่าใหม่ จากตารางค่าของสถานการณ์แสงทั่วไป ฉันได้คิดค้นสูตรที่เรียบง่ายมากเพื่อแปลงค่าลักซ์เป็นค่าระหว่าง 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!