حاسبة التصميم

محاولة محاكاة حاسبة شمسية على الويب باستخدام Window Controls Overlay API وAmbient Light Sensor API

التحدي

أنا من مواليد الثمانينيات. عندما كنت في المدرسة الثانوية، كانت الآلات الحاسبة التي تعمل بالطاقة الشمسية رائجة جدًا. حصلنا جميعًا على آلة حاسبة TI-30X SOLAR من المدرسة، ولديّ ذكريات جميلة عن مقارنة آلاتنا الحاسبة ببعضها البعض من خلال حساب عاملي العدد 69، وهو أكبر عدد يمكن أن تتعامل معه الآلة الحاسبة TI-30X. (كان تفاوت السرعة قابلاً للقياس بشكل كبير، ولا أعرف السبب حتى الآن).

بعد مرور 28 عامًا تقريبًا، أردت أن أشارك في تحدي Designcember الممتع وأعيد إنشاء الآلة الحاسبة باستخدام HTML وCSS وJavaScript. بما أنّني لست مصمّمًا محترفًا، لم أبدأ من الصفر، بل استخدمت 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 في بيان تطبيق الويب.

{
  "display": "fullscreen"
}

على الأجهزة التي تحتوي على فتحة كاميرا أو نتوء، يؤدي تعديل منفذ العرض ليغطي المحتوى الشاشة بأكملها إلى إظهار التطبيق بشكل رائع.

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

آلة حاسبة Designcember تعمل بملء الشاشة على هاتف Pixel 6 Pro

المزج مع سطح المكتب

على الكمبيوتر المكتبي، يمكنني استخدام ميزة رائعة هي: تراكب عناصر التحكّم في النوافذ، والتي تتيح لي وضع المحتوى في شريط العنوان لنافذة التطبيق. تتمثل الخطوة الأولى في إلغاء تسلسل الاحتياطي لوضع العرض حتى يحاول استخدام window-controls-overlay أولاً عندما يكون متاحًا.

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

يؤدي ذلك إلى إخفاء شريط العنوان بشكل فعّال وانتقال المحتوى إلى منطقة شريط العنوان كما لو كان شريط العنوان غير موجود. أقترح نقل خلية الطاقة الشمسية ذات التصميم المحاكي إلى شريط العنوان ونقل بقية واجهة مستخدم الآلة الحاسبة إلى الأسفل وفقًا لذلك، ويمكنني إجراء ذلك باستخدام بعض رموز CSS التي تستخدم متغيّرات البيئة titlebar-area-*. ستلاحظ أنّ جميع أدوات الاختيار تحمل فئة 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 القديمة. يمكنني الآن وضع الآلة الحاسبة بحرية على سطح المكتب وتفعيل ميزة عناصر التحكّم في النافذة من خلال النقر على السهم في أعلى يسار الشاشة.

تشغيل آلة حاسبة Designcember في الوضع المستقل مع تفعيل ميزة &quot;تراكب عناصر التحكم في النوافذ&quot; تعرض الشاشة كلمة &quot;Google&quot; بأبجدية الآلة الحاسبة.

خلية شمسية تعمل بالفعل

ولإضفاء لمسة من التميّز، كان عليّ بالطبع أن أجعل الخلية الشمسية تعمل فعليًا. يجب أن تعمل الآلة الحاسبة فقط إذا كان هناك ضوء كافٍ. لقد صمّمتُ هذا النموذج من خلال ضبط CSS opacity للأرقام المعروضة باستخدام متغيّر CSS --opacity يمكنني التحكّم فيه من خلال JavaScript.

:root {
  --opacity: 0.75;
}

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

للتأكّد من توفّر إضاءة كافية لعمل الآلة الحاسبة، أستخدم واجهة برمجة التطبيقات AmbientLightSensor. لكي تكون واجهة برمجة التطبيقات هذه متاحة، كان عليّ ضبط العلامة #enable-generic-sensor-extra-classes في about:flags وطلب الإذن 'ambient-light-sensor'. كما كان الحال من قبل، أستخدم التحسين التدريجي لتحميل الرمز ذي الصلة فقط عندما تكون واجهة برمجة التطبيقات متوافقة.

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 سعيد!