實現視窗之間的狀態共享,無需依賴伺服器!

Mondo 科技 更新 2024-01-31

最近,社交網路上出現了乙個 gif **,展示了 Bjorn Staal 製作的令人驚嘆的藝術作品。

我想重現它,但缺乏球體、粒子和物理學方面的 3D 技能,我的目標是了解如何讓乙個視窗對另乙個視窗的位置做出反應。

從本質上講,在多個視窗之間共享狀態,我發現這是 bjorn 專案最酷的方面之一!

由於我找不到關於該主題的好文章或教程,我決定與您分享我的發現。

讓我們嘗試基於 BJORN 的工作建立乙個簡化的概念驗證 (PoC)!

我做的第一件事是列出我所知道的在多個客戶之間共享資訊的所有方式:

顯然,擁有乙個伺服器(帶有輪詢或 websocket)可以簡化問題。 但是,這是不可能的,因為 bjorn 在不使用伺服器的情況下取得了成果。

本地儲存本質上是瀏覽器鍵值儲存,通常用於在瀏覽器會話之間儲存資訊。 雖然通常用於儲存身份驗證令牌或重定向 URL,但它可以儲存任何可序列化的內容。 您可以在此處了解更多資訊。

我最近發現了一些有趣的本地儲存 API,包括每當本地儲存被同一**的另乙個會話更改時觸發的儲存事件。

我們可以通過將每個視窗的狀態儲存在本地儲存中來利用這一點。 每當乙個視窗更改其狀態時,其他視窗都會通過儲存事件進行更新。

這是我最初的想法,這似乎是 bjorn 選擇的解決方案,因為他在這裡分享了他的本地儲存管理器**以及將其與 threejs 一起使用的示例。

但當我發現有**可以解決這個問題時,我想看看有沒有別的辦法......劇透警告:是的,有!

在這個華麗的術語背後是乙個引人入勝的概念——網路工作者的概念。

簡單來說,工作執行緒本質上是在另乙個執行緒上執行的第二個指令碼。 雖然它們無法訪問 DOM(因為它們存在於 HTML 文件之外),但它們仍然可以與您的主指令碼進行通訊。

它們主要用於通過處理後台作業(例如預取資訊)或處理不太重要的任務(例如流日誌和輪詢)來解除安裝主指令碼。

共享工作執行緒是一種特殊型別的網路工作執行緒,可以與同一指令碼的多個例項進行通訊,這使得它們對我們的用例很有意義!好吧,讓我們直接跳進去吧!

如前所述,worker 是“第二個指令碼”,有自己的切入點。 根據您的設定(TypeScript、程式、Dev Server),您可能需要調整 tsconfig、新增指令或使用特定的匯入語法。

我無法涵蓋使用 Web Worker 的所有可能方法,但您可以在 mdn 或網際網絡上找到資訊。

如果需要,我很樂意為本文寫前傳,詳細說明設定它們的所有方法!

就我而言,我使用 vite 和打字稿,所以我需要乙個工人ts 檔案並將其安裝@types sharedworker 作為開發依賴項。 我們可以使用以下語法在主指令碼中建立連線:

new sharedworker(new url("worker.ts", import.meta.url));
基本上,我們需要:

識別每個視窗會跟蹤所有視窗狀態,一旦乙個視窗改變狀態,提醒其他視窗重繪我們的狀態將非常簡單:

type windowstate = ;
當然,最重要的資訊是視窗screenxthemwindow。Screeny 告訴我們視窗相對於顯示器左上角的位置。

我們將有兩種型別的訊息:

每當每個視窗的狀態發生更改時,都會發布乙個包含其新狀態的 WindowStateChangedMessage。 工作人員將向所有其他視窗傳送更新,以提醒他們其中乙個視窗已更改。 工作執行緒將傳送包含所有視窗狀態的 syncmessage。 我們可以從乙個普通工人開始,他看起來有點像這樣:

// worker.ts let windows: [= onconnect = ()=> ;
我們與 sharedworker 的基本連線如下所示。 我有一些基本函式可以生成 id 並計算當前視窗狀態,並且我還有一些輸入到一種訊息型別中,我們可以將其稱為 workermessage:

// main.ts import from "./types"; import from "./windowstate"; const sharedworker = new sharedworker(new url("worker.ts", import.meta.url));let currentwindow = getcurrentwindowstate();let id = generateid();
一旦我們啟動應用程式,我們應該提醒工作人員有乙個新視窗,所以我們立即傳送一條訊息:

// main.ts sharedworker.port.postmessage(, satisfies workermessage);
我們可以在工作端監聽此訊息並相應地更改 onmessage。 基本上,一旦工作執行緒收到 WindowStateChanged 訊息,它要麼是乙個新視窗,我們將其附加到狀態,要麼是乙個已更改的舊視窗。 那麼我們應該提醒大家,狀態已經改變了:

// worker.ts port.onmessage = function (event: messageevent) = msg.payload; const oldwindowindex = windows.findindex((w) => w.id === id); if (oldwindowindex !== -1) else );windows.foreach((w) => // send sync here );break; }
為了傳送同步,我實際上需要一些技巧,因為“port”屬性無法序列化,所以我將其字串化並解析回來。 因為我很懶,所以我不只是將視窗對映到更可序列化的陣列:

w.port.postmessage(, satisfies workermessage);
現在是時候畫點東西了!

當然,我們不做複雜的 3D 球體:我們只會在每個視窗的中心畫乙個圓圈,並在球體之間畫一條線!

我將使用 HTML Canvas 的基本 2D 上下文進行繪製,但您可以使用任何您想要的東西。 畫乙個圓圈,很簡單:

const drawcentercircle = (ctx: canvasrenderingcontext2d, center: coordinates) => = center; ctx.strokestyle = "#eeeeee"; ctx.linewidth = 10; ctx.beginpath();ctx.arc(x, y, 100, 0, math.pi * 2, false); ctx.stroke();ctx.closepath();
為了畫線,我們需要做一些數學運算(我保證,這不是很多),將另乙個視窗中心的相對位置轉換為當前視窗的坐標。

基本上,我們正在改變基礎。 我用一點數學來做。 首先,我們將基數更改為在顯示器上有坐標,由當前視窗 screenx screeny 偏移。

const basechange = (:=> ;const currentwindowcoordinate = ; return currentwindowcoordinate; }
如您所知,現在我們在同乙個相對坐標系中有兩個點,現在可以畫線了!

const drawconnectingline = (:=> ;const targetwindowoffset: coordinates = ; const origin = getwindowcenter(hostwindow); const target = getwindowcenter(targetwindow); const targetwithbasechange = basechange();ctx.strokestyle = "#ff0000"; ctx.linecap = "round"; ctx.beginpath();ctx.moveto(origin.x, origin.y); ctx.lineto(targetwithbasechange.x, targetwithbasechange.y); ctx.stroke();ctx.closepath();
現在,我們只需要對狀態變化做出反應。

// main.ts sharedworker.port.onmessage = (event: messageevent) => )=> )
作為最後一步,我們只需要定期檢查視窗是否發生變化,如果發生變化,則傳送訊息。

setinterval(()=> )satisfies workermessage); currentwindow = newwindow; }100);
您可以在此儲存庫中找到完整的 **。 實際上,我已經做了很多實驗來使它更抽象,但要點是一樣的。

如果您在多個視窗上執行它,希望您得到與此相同的結果!

相關問題答案

    無邊框視窗模式突破束縛,實現更多可能

    當我們開啟應用程式時,我們通常會在視窗邊緣看到乙個邊框。這些邊框不僅提供了視覺邊界,還允許我們調整大小 最小化 最大化或關閉視窗等。但是,有些應用程式允許我們將其設定為無邊框視窗模式,那麼這種模式究竟是做什麼用的呢?.增強沉浸感。無邊框視窗模式最顯著的特點是它消除了視窗邊框的約束。對於一些需要全屏使...

    使用 Hooks 重用和封裝狀態邏輯

    在 React 中,在元件之間重用狀態邏輯一直是乙個挑戰。React 本身並沒有提供一種直接的方式來將可重用的行為附加到元件上,例如將元件連線到儲存。如果你已經使用 React 一段時間了,你可能會熟悉這個問題的一些解決方案,比如渲染道具和高階元件。但是,這些方案需要重新組織元件結構,這可能難以理解...

    發展集體經濟,實現團隊共贏

    發展集體經濟的重要性。在現代社會中,集體經濟的發展已成為實現團隊雙贏的重要途徑。隨著經濟全球化的深入和人們思維觀念的轉變,集體經濟逐漸受到廣大團隊的關注和重視。集體經濟的發展不僅是為了個人的利益,更是為了整個團隊的共同繁榮和可持續發展。通過集體經濟的發展,可以達到團隊每個成員共同進步 共同發展的目標...

    真?東方小孫回到直播間,狀態良好,直播間10萬人

    東方小孫回到東方精選直播間,狀態似乎不錯,直播間的人數也達到了萬 們真的是驚豔了。但真正支援東方小孫的又有多少人呢?他們中有多少人是來看他嘲笑的?他們中有多少是鑄造的?如果董宇輝的離開對直播間沒有影響,那絕對是不可能的。因此,東方小孫的回歸也承受著很大的壓力,他和直播間的主播們都在竭盡全力留住更多的...

    裝修合作平台,實現共贏的裝修合作模式

    裝飾合作平台作為一種創新的裝修合作模式,通過整合資源 優化流程 提高效率,實現了業主 裝修公司 設計師 建材供應商等各方的共贏。 通用裝修合作平台。裝修訂單寶app 裝修服務商通過APP檢視業主的詳細需求和要求,並與業主進行溝通交流,提高了業務效率,降低了溝通成本。房屋裝飾設計應用程式 提供專業的設...