Máy tính Designcember

Một nỗ lực biến hình để tạo lại máy tính năng lượng mặt trời trên web bằng API Lớp phủ chế độ điều khiển cửa sổ và API Cảm biến ánh sáng xung quanh.

Thách thức

Tôi là đứa trẻ thập niên 1980. Khi còn học trung học, có một điều khiến mọi người háo hức là máy tính năng lượng mặt trời. Tất cả chúng tôi đều được nhà trường tặng một SOLAR TI-30X, và tôi rất nhớ việc chúng tôi đo điểm chuẩn giữa các máy tính với nhau bằng cách tính giai thừa của 69, con số cao nhất mà TI-30X có thể xử lý. (Chênh lệch tốc độ rất có thể đo lường được, tôi vẫn không biết lý do tại sao.)

Giờ đây, gần 28 năm sau, tôi nghĩ sẽ là một thử thách Designcember thú vị để tạo lại máy tính trong HTML, CSS và JavaScript. Không có nhiều khả năng thiết kế, tôi không bắt đầu từ con số không mà chỉ là sử dụng CodePen của Sassja Ceballos.

Chế độ xem CodePen với các bảng điều khiển HTML, CSS và JS xếp chồng ở bên trái và bản xem trước công cụ tính ở bên phải.

Cài đặt dễ dàng

Mặc dù khởi đầu không tệ, nhưng tôi đã quyết định phát huy tối đa sự ấn tượng theo phong cách đa dạng. Bước đầu tiên là đặt ứng dụng thành PWA để có thể cài đặt. Tôi duy trì mẫu PWA cơ sở trên Glitch mà tôi có thể phối lại mỗi khi cần bản minh hoạ nhanh. Trình chạy dịch vụ của trình chạy này sẽ không mang lại cho bạn bất kỳ phần thưởng lập trình nào và chắc chắn trình chạy này chưa sẵn sàng phát hành công khai, nhưng bạn chỉ cần kích hoạt thanh thông tin nhỏ của Chromium để cài đặt ứng dụng.

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');
      }
    })(),
  );
});

Kết hợp với thiết bị di động

Giờ đây, khi ứng dụng có thể cài đặt được, bước tiếp theo là làm cho ứng dụng tương thích nhất có thể với các ứng dụng của hệ điều hành. Trên thiết bị di động, tôi có thể thực hiện việc này bằng cách đặt chế độ hiển thị thành fullscreen trong Tệp kê khai ứng dụng web.

{
  "display": "fullscreen"
}

Trên các thiết bị có lỗ camera hoặc khía cạnh, hãy điều chỉnh khung nhìn để nội dung bao phủ toàn bộ màn hình khiến ứng dụng trở nên tuyệt đẹp.

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

Công cụ tính Designcember chạy ở chế độ toàn màn hình trên điện thoại Pixel 6 Pro.

Kết hợp với máy tính để bàn

Trên máy tính, tôi có thể sử dụng một tính năng thú vị: Lớp phủ điều khiển cửa sổ. Tính năng này cho phép tôi đặt nội dung vào thanh tiêu đề của cửa sổ ứng dụng. Bước đầu tiên là ghi đè trình tự dự phòng của chế độ hiển thị để cố gắng sử dụng window-controls-overlay trước khi có sẵn.

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

Điều này giúp thanh tiêu đề biến mất một cách hiệu quả và nội dung di chuyển lên trên khu vực thanh tiêu đề như thể thanh tiêu đề không có ở đó. Ý tưởng của tôi là di chuyển pin mặt trời đa hình lên thanh tiêu đề và phần còn lại của giao diện người dùng máy tính xuống tương ứng. Tôi có thể thực hiện việc này với một số CSS sử dụng biến môi trường titlebar-area-*. Bạn sẽ nhận thấy rằng tất cả bộ chọn đều có lớp wco, có liên quan đến một vài đoạn dưới.

#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);
}

Tiếp theo, tôi cần quyết định thành phần nào có thể kéo được, vì thanh tiêu đề mà tôi thường dùng để kéo không có sẵn. Theo kiểu tiện ích cổ điển, tôi thậm chí có thể kéo toàn bộ công cụ tính bằng cách áp dụng (-webkit-)app-region: drag, ngoài các nút nhận được (-webkit-)app-region: no-drag nên không thể dùng để kéo.

#calc_inside.wco,
#calc_solar_cell.wco {
  -webkit-app-region: drag;
  app-region: drag;
}

button {
  -webkit-app-region: no-drag;
  app-region: no-drag;
}

Bước cuối cùng là làm cho ứng dụng phản ứng với các thay đổi về lớp phủ điều khiển cửa sổ. Trong phương pháp nâng cao thực tế, tôi chỉ tải mã cho tính năng này khi trình duyệt hỗ trợ.

if ('windowControlsOverlay' in navigator) {
  import('/wco.js');
}

Mỗi khi cửa sổ điều khiển cửa sổ có hình dạng lớp phủ thay đổi, tôi sẽ sửa đổi ứng dụng để sao cho nó trông tự nhiên nhất có thể. Bạn nên gỡ lỗi sự kiện này vì sự kiện này có thể được kích hoạt thường xuyên khi người dùng đổi kích thước cửa sổ. Cụ thể là tôi áp dụng lớp wco cho một số phần tử, vì vậy, CSS của tôi từ trên bắt đầu và tôi cũng thay đổi màu giao diện. Tôi có thể phát hiện xem lớp phủ kiểm soát cửa sổ có hiển thị hay không bằng cách kiểm tra thuộc tính 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();

Giờ đây, với tất cả những tính năng này đã sẵn sàng, tôi sẽ có một tiện ích tính toán gần giống như Winamp cổ điển với một trong các chủ đề Winamp cũ. Bây giờ, tôi có thể tự do đặt công cụ tính trên máy tính của mình và kích hoạt tính năng điều khiển cửa sổ bằng cách nhấp vào hình chữ v ở góc trên bên phải.

Công cụ Designcember chạy ở chế độ độc lập và đang bật tính năng Lớp phủ chế độ điều khiển cửa sổ. Màn hình hiển thị dòng chữ &quot;Google&quot; trong bảng chữ cái máy tính.

Một pin mặt trời đang hoạt động bình thường

Để thực sự hài lòng, tất nhiên tôi phải làm cho pin mặt trời thực sự hoạt động. Máy tính chỉ hoạt động được nếu có đủ ánh sáng. Tôi đã lập mô hình hoá bằng cách đặt CSS opacity của các chữ số trên màn hình thông qua biến CSS --opacity mà tôi kiểm soát thông qua JavaScript.

:root {
  --opacity: 0.75;
}

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

Để phát hiện xem máy tính có đủ ánh sáng hay không, tôi sử dụng API AmbientLightSensor. Để có API này, tôi cần đặt cờ #enable-generic-sensor-extra-classes trong about:flags và yêu cầu quyền 'ambient-light-sensor'. Như trước đây, tôi sử dụng tính năng nâng cao tăng dần để chỉ tải mã có liên quan khi API này được hỗ trợ.

if ('AmbientLightSensor' in window) {
  import('/als.js');
}

Cảm biến trả về độ sáng của môi trường xung quanh theo đơn vị lux bất cứ khi nào có chỉ số mới. Dựa trên bảng giá trị của các tình huống ánh sáng thông thường, tôi đã nghĩ ra một công thức rất đơn giản để chuyển đổi giá trị lux thành giá trị nằm trong khoảng từ 0 đến 1 mà tôi sẽ gán cho biến --opacity theo phương thức lập trình.

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();
  }
})();

Trong video dưới đây, bạn có thể thấy máy tính bắt đầu hoạt động như thế nào sau khi tôi tăng ánh sáng trong phòng lên đủ. Vậy là bạn đã có kết quả: một máy tính năng lượng mặt trời đa dạng thực sự hoạt động. Những thử nghiệm TI-30X SOLAR cũ của tôi đã trải qua một chặng đường dài.

Bản minh hoạ

Đừng quên xem bản minh hoạ của Designcember Calculator và xem mã nguồn trên Glitch. (Để cài đặt ứng dụng, bạn cần mở ứng dụng đó trong cửa sổ riêng. Phiên bản được nhúng bên dưới sẽ không kích hoạt thanh thông tin nhỏ.)

Design (Nhà thiết kế) vui vẻ!