談談外網刷屏的量子糾纏效果
大家好,我卡頌。
最近被一段酷炫的量子糾纏效果刷屏了:
原作者是@_nonfigurativ_,一位藝術家、程序員。
今天簡單講講他的核心原理。
基礎概念
首先我們需要知道兩個概念:
- 屏幕坐標系,屏幕左上角就是「屏幕坐標系」的圓點。
- 窗口坐標系,頁面窗口左上角就是「窗口坐標系」的圓點。
如果只用一臺電腦,不外接屏幕的話,我們會有:
- 一個屏幕坐標系
- 打開幾個頁面,每個頁面有各自的窗口坐標系
如果外接了屏幕(或外接pad),那么就存在多個屏幕坐標系,這種情況的計算需要用到「管理屏幕設備的API」 —— window.getScreenDetails[1],在本文的討論中不涉及這種情況。
當我們打開一個新頁面窗口,窗口的左上角就是窗口坐標系的圓點,如果要在頁面正中間畫個圓,那圓心的窗口坐標系坐標應該是(window.innerWidth / 2, window.innerHeight / 2)。
對于一個打開的窗口:
- 他的左上角相對于屏幕頂部的距離為window.screenTop。
- 他的左上角相對于屏幕左邊的距離為window.screenLeft。
所以,我們可以輕松得出圓的圓心在「屏幕坐標系」中的坐標:
位置檢測
在效果中,當打開兩個頁面,他們能感知到對方的位置并作出反應,這是如何實現的呢?
當前,我們已經知道圓心在「屏幕坐標系」中的坐標。如果打開多個頁面,就會獲得多個「圓心的屏幕坐標系坐標」。
現在需要做的,就是讓這些頁面互相知道對方的坐標,這樣就能向對應的方向做出連接的特效。
同源網站跨頁面通信的方式有很多,比如:
- Window.postMessage
- LocalStorage、SessionStorage
- SharedWorker
- BroadcastChannel
甚至Cookie也能用于跨頁面通信(可以在同源的所有頁面之間共享)。
在這里作者使用的是LocalStorage:
只需要為每個頁面生成一個唯一ID:
const pageId = Math.random().toString(36).substring(2); // 生成一個隨機的頁面ID
每當將圓心最新坐標存儲進LocalStorage時:
localStorage.setItem(
pageId,
JSON.stringify({
x: window.screenX,
y: window.screenY,
width: window.innerWidth,
height: window.innerHeight,
})
);
在另一個頁面通過監(jiān)聽storage事件就能獲取「對方圓心的屏幕坐標系坐標」:
window.addEventListener("storage", (event) => {
if (event.key !== pageId) {
// 來自另一個頁面
const { x, y } = JSON.parse(event.newValue);
// ...
}
});
再將對方「圓心的屏幕坐標系坐標」轉換為自身的「窗口坐標系坐標」,并在該坐標繪制一個圓,就能達到類似「窗口疊加后,下面窗口的畫面出現在上面窗口內」的效果。
通俗的講,所有頁面都會繪制其他頁面的圓,只是有些圓在頁面窗口外,看不見罷了。
考慮到頁面性能,「檢測圓心的屏幕坐標系坐標」、「渲染圓」相關操作可以放到requestAnimationFrame回調中執(zhí)行。
后記
上述只是該效果的核心原理。要完全復刻效果,還得考慮:
- 渲染大量粒子(我們示例中用「圓」代替),且多窗口通信時的性能問題。
- 窗口移動時的阻尼效果。
- 當前的實現是在同一個屏幕坐標系中,如果要跨屏幕實現,需要使用window.getScreenDetails。
不得不感嘆跨界(作者是藝術家 + 程序員)迸發(fā)的想象力真的不一般。
參考資料
[1]window.getScreenDetails:https://developer.chrome.com/zh/articles/multi-screen-window-placement/。