ماشین حساب Designcember

تلاشی مبهم برای بازآفرینی یک ماشین حساب خورشیدی در وب با Windows Controls Overlay API و Ambient Light Sensor API.

چالش

من یک بچه دهه 1980 هستم. چیزی که در دوران دبیرستان مورد توجه من بود، ماشین حساب های خورشیدی بودند. مدرسه به همه ما یک TI-30X SOLAR داده است، و من خاطرات خوبی از ما دارم که با محاسبه فاکتوریل 69، ماشین‌حساب‌هایمان را با یکدیگر مقایسه می‌کردیم، بالاترین عددی که TI-30X می‌توانست تحمل کند. (واریانس سرعت بسیار قابل اندازه گیری بود، من هنوز نمی دانم چرا.)

اکنون، تقریباً 28 سال بعد، فکر کردم که ایجاد مجدد ماشین حساب در HTML، CSS و جاوا اسکریپت، چالشی سرگرم کننده در Designcember است. از آنجایی که زیاد یک طراح نبودم، از ابتدا شروع نکردم، بلکه با یک CodePen توسط Sassja Ceballos شروع کردم.

نمایش 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 ، که به من امکان می دهد محتوا را در نوار عنوان پنجره برنامه قرار دهم. اولین گام این است که توالی بازگشتی حالت نمایش را نادیده بگیریم تا زمانی که در دسترس است سعی شود ابتدا از window-controls-overlay استفاده کند.

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

این باعث می شود که نوار عنوان به طور موثری از بین برود و محتوا به سمت قسمت نوار عنوان به سمت بالا حرکت کند، گویی نوار عنوان آنجا نیست. ایده من این است که سلول خورشیدی skeuomorphic را به سمت بالا به نوار عنوان و بقیه UI ماشین حساب را به پایین منتقل کنم، که می توانم با برخی از 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 فعال است. صفحه نمایش در حروف الفبای ماشین حساب عبارت «Google» را نشان می دهد.

یک سلول خورشیدی در واقع در حال کار

برای تدبیر نهایی، من البته نیاز داشتم که سلول خورشیدی را واقعاً کار کنم. ماشین حساب فقط در صورتی باید کار کند که نور کافی وجود داشته باشد. روشی که من این را مدل کردم از طریق تنظیم opacity CSS ارقام روی نمایشگر از طریق یک متغیر CSS --opacity است که از طریق جاوا اسکریپت کنترل می کنم.

:root {
  --opacity: 0.75;
}

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

برای تشخیص اینکه آیا نور کافی برای کارکرد ماشین حساب در دسترس است یا نه، از AmbientLightSensor API استفاده می کنم. برای اینکه این 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 Calculator بازی کنید و کد منبع را در Glitch بررسی کنید. (برای نصب برنامه، باید آن را در پنجره خودش باز کنید. نسخه جاسازی شده زیر نوار اطلاعات کوچک را فعال نمی کند.)

دیزاین مبارک!