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

محاولة نموذجية لإعادة إنشاء آلة حاسبة بالطاقة الشمسية على الويب باستخدام واجهة برمجة التطبيقات Window Controls Overlay API وAPI (واجهة برمجة التطبيقات لمستشعر الضوء المحيط).

التحدي

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

والآن وبعد 28 عامًا تقريبًا، اعتقدت أن إعادة إنشاء الآلة الحاسبة في HTML وCSS وJavaScript سيكون تحديًا ممتعًا لدى Designcember. لم أبدأ تصميمي من نقطة الصفر، ولكنني اعتمدت على برنامج CodePen من تصميم ساسيا سيبالوس.

عرض CodePen مع لوحات HTML وCSS وJS مكدسة على اليمين ومعاينة الآلة الحاسبة على اليسار.

جعله قابلاً للتثبيت

على الرغم من أن البداية لم تكن سيئة، إلا أنني قررت الاستفادة من هذا المستوى من المتعة الكاملة. كانت الخطوة الأولى هي جعله تطبيق ويب تقدّمي (PWA) بحيث يمكن تثبيته. أحتفظ بنموذج 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 في وضع ملء الشاشة على هاتف 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 في وضع مستقل مع تفعيل ميزة Window Controls Overlay (تراكب عناصر التحكّم). تلفظ الشاشة كلمة &quot;Google&quot; بالأحرف الأبجدية الإنجليزية.

خلية شمسية قيد التشغيل

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

نتمنى لك قضاء Designcember.