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

محاولة لإعادة إنشاء آلة حاسبة شمسية على الويب باستخدام واجهتَي برمجة التطبيقات 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) حتى يمكن تثبيته. أحافظ على نموذج أساسي لتطبيق الويب التقدّمي على Glitch وأُعيد مزجه كلما احتجت إلى عرض توضيحي سريع. لن يفوز عامل الخدمة الخاص بالتطبيق بأي جائزة برمجة، وهو غير جاهز للإصدار العلني، ولكنه كافٍ لتشغيل شريط المعلومات المصغّر في 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" />

تطبيق Designcember Calculator معروض على الشاشة الكاملة على هاتف 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 Calculator تعمل في وضع مستقل مع تفعيل ميزة &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');
}

يعرض جهاز الاستشعار قياسات الضوء المحيط بوحدات 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. (لِ تثبيت التطبيق، عليك فتحه في نافذته الخاصة. لن يؤدي الإصدار المضمّن أدناه إلى بدء شريط المعلومات المصغّر.)

مع أطيب التحيّات،