成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

原來 WebWorker 還可以做這么酷的事情!

開發(fā) 前端
worker是“第二個(gè)腳本”。我們需要根據(jù)設(shè)置(TypeScript、捆綁器、開發(fā)服務(wù)器),調(diào)整tsconfig、添加指令或使用特定的導(dǎo)入語法。

最近,我看到這樣一件令人驚嘆的藝術(shù)品:

酷斃了,是不是?看著心癢難耐,于是我試著重建,發(fā)現(xiàn)這個(gè)項(xiàng)目的本質(zhì)是——在多個(gè)窗口之間共享狀態(tài)。這個(gè)可以有!

一起來看看我的研究經(jīng)過吧。

首先聲明,我這個(gè)是簡(jiǎn)化版的哈!

我做的第一件事是列出我所知道的在多個(gè)客戶端之間共享信息的所有方法:

服務(wù)器

顯然,擁有服務(wù)器可以直接簡(jiǎn)化問題。但是,由于這個(gè)項(xiàng)目需要在不使用服務(wù)器的情況下實(shí)現(xiàn),所以直接pass這個(gè)選項(xiàng)。

本地存儲(chǔ)

本地存儲(chǔ)本質(zhì)上是瀏覽器鍵值存儲(chǔ),通常用于在瀏覽器會(huì)話之間持久保存信息。雖然本地存儲(chǔ)常用于存儲(chǔ)身份驗(yàn)證令牌或重定向URL,但可以存儲(chǔ)任何可序列化的內(nèi)容。

這里重點(diǎn)要介紹一個(gè)非常有趣的本地存儲(chǔ)API,storage事件——每當(dāng)本地存儲(chǔ)由于同一網(wǎng)站上的另一個(gè)會(huì)話而更改時(shí),就會(huì)觸發(fā)事件。

圖片圖片

我靈機(jī)一動(dòng)試著在本地存儲(chǔ)中存儲(chǔ)每個(gè)窗口的狀態(tài),每當(dāng)有窗口的狀態(tài)改變時(shí),其他窗口就通過storage事件進(jìn)行更新。

對(duì)頭,似乎解決方案就是這個(gè)了。

那么代碼可以解決這個(gè)問題嗎?到底有沒有其他方法呢?答案是:有!

共享worker

在這個(gè)華麗的術(shù)語背后,我們需要先了解WebWorkers的概念。

簡(jiǎn)單來說,worker其實(shí)是在另一個(gè)線程上運(yùn)行的第二個(gè)腳本。雖然因?yàn)榇嬖谟贖TML文檔之外導(dǎo)致worker無權(quán)訪問 DOM,但worker仍然可以與主腳本進(jìn)行通信。

worker主要用于通過處理后臺(tái)作業(yè),例如預(yù)獲取信息或處理不太重要的任務(wù)(如流式日志和輪詢),來卸載主腳本。

圖片圖片

共享worker是一種特殊的WebWorker,它可以與同一腳本的多個(gè)實(shí)例進(jìn)行通信,可以將信息發(fā)送到同一腳本的多個(gè)會(huì)話!

圖片圖片

設(shè)置worker

如前所述,worker是“第二個(gè)腳本”。我們需要根據(jù)設(shè)置(TypeScript、捆綁器、開發(fā)服務(wù)器),調(diào)整tsconfig、添加指令或使用特定的導(dǎo)入語法。

在眾多使用WebWorker的方法中,就我而言,我喜歡使用Vite和TypeScript,所以需要一個(gè)worker.ts文件并將@types/sharedworker安裝為開發(fā)依賴項(xiàng)。

我們可以使用以下語法在主腳本中創(chuàng)建連接:

new SharedWorker(new URL("worker.ts", import.meta.url));

整個(gè)過程就是:

1.識(shí)別每個(gè)窗口

2.跟蹤所有窗口狀態(tài)

3.在窗口改變狀態(tài)時(shí)提醒其他窗口重新繪制

狀態(tài)也是非常簡(jiǎn)單:

type WindowState = {
      screenX: number; // window.screenX
      screenY: number; // window.screenY
      width: number; // window.innerWidth
      height: number; // window.innerHeight
};

當(dāng)然,最關(guān)鍵的信息是window.screenX和window.screenY,因?yàn)樾枰鼈兏嬖V我們窗口相對(duì)于顯示器左上角的位置。

我們還需要提供兩種類型的消息:

1.每當(dāng)有窗口改變狀態(tài),發(fā)布帶有新狀態(tài)的windowStateChangedmessage。

2.Worker需要向所有其他窗口發(fā)送更新,提醒有窗口已發(fā)生更改。Worker還需要發(fā)送包含所有窗口狀態(tài)的syncmessage。

我們的代碼先從平凡的worker開始,就像這樣:

// worker.ts 
    let windows: { windowState: WindowState; id: number; port: MessagePort }[] = [];
 
    onconnect = ({ ports }) => {
      const port = ports[0];
 
      port.onmessage = function (event: MessageEvent<WorkerMessage>) {
        console.log("We'll do something");
      };
    };

我們與SharedWorker的基本連接如下所示。這里我通過基本函數(shù)生成id,并計(jì)算當(dāng)前窗口狀態(tài)。我還對(duì)可以使用的稱為WorkerMessage的Message類型進(jìn)行了輸入:

// main.ts
   import { WorkerMessage } from "./types";
   import {
     generateId,
     getCurrentWindowState,
   } from "./windowState";

   const sharedWorker = new SharedWorker(new URL("worker.ts", import.meta.url));
   let currentWindow = getCurrentWindowState();
   let id = generateId();

一啟動(dòng)應(yīng)用程序,就得提醒worker有新窗口,所以我們要立即發(fā)送消息:

// main.ts 
    sharedWorker.port.postMessage({
      action: "windowStateChanged",
      payload: {
        id,
        newWindow: currentWindow,
      },
    } satisfies WorkerMessage);

我們可以在worker端監(jiān)聽此消息,并相應(yīng)地更改onmessage。這個(gè)流程就是,一旦worker收到windowStateChanged消息,那么意味著要么是有新窗口,需要將其附加到狀態(tài),要么舊窗口已發(fā)生更改。因此需要提醒大家,狀態(tài)已發(fā)生了改變:

// worker.ts
    port.onmessage = function (event: MessageEvent<WorkerMessage>) {
      const msg = event.data;
      switch (msg.action) {
        case "windowStateChanged": {
          const { id, newWindow } = msg.payload;
          const oldWindowIndex = windows.findIndex((w) => w.id === id);
          if (oldWindowIndex !== -1) {
            // old one changed
            windows[oldWindowIndex].windowState = newWindow;
          } else {
            // new window 
            windows.push({ id, windowState: newWindow, port });
          }
          windows.forEach((w) =>
            // send sync here 
          );
          break;
        }
      }
    };

發(fā)送同步需要一些技巧,因?yàn)閜ort屬性無法序列化,所以我將其字符串化再解析回來。

w.port.postMessage({
      action: "sync",
      payload: { allWindows: JSON.parse(JSON.stringify(windows)) },
    } satisfies WorkerMessage);

接下來就是繪圖了!

有趣的部分:繪圖!

復(fù)雜的3D球體就饒了我吧,我只打算在每個(gè)窗口的中心畫一個(gè)圓圈,然后再在球體之間畫一條線意思意思!

我使用HTML Canvas的基本2D上下文進(jìn)行繪制,當(dāng)然你也可以使用其他方式繪制。反正就是畫圓圈,非常簡(jiǎn)單:

const drawCenterCircle = (ctx: CanvasRenderingContext2D, center: Coordinates) => {
      const { x, y } = center;
      ctx.strokeStyle = "#eeeeee";
      ctx.lineWidth = 10;
      ctx.beginPath();
      ctx.arc(x, y, 100, 0, Math.PI * 2, false);
      ctx.stroke();
      ctx.closePath();
    };

至于畫線,就需要做一些數(shù)學(xué)運(yùn)算了——將另一個(gè)窗口中心的相對(duì)位置轉(zhuǎn)換為當(dāng)前窗口上的坐標(biāo)。

首先改變基準(zhǔn)使顯示器具有坐標(biāo),并根據(jù)當(dāng)前窗口screenX/screenY進(jìn)行偏移。

圖片圖片

const baseChange = ({
      currentWindowOffset,
      targetWindowOffset,
      targetPosition,
    }: {
      currentWindowOffset: Coordinates;
      targetWindowOffset: Coordinates;
      targetPosition: Coordinates;
    }) => {
      const monitorCoordinate = {
        x: targetPosition.x + targetWindowOffset.x,
        y: targetPosition.y + targetWindowOffset.y,
      };
 
      const currentWindowCoordinate = {
        x: monitorCoordinate.x - currentWindowOffset.x,
        y: monitorCoordinate.y - currentWindowOffset.y,
      };
 
      return currentWindowCoordinate;
    };

看,同一個(gè)相對(duì)坐標(biāo)系上有兩個(gè)點(diǎn),可以畫線了!

const drawConnectingLine = ({
      ctx,
      hostWindow,
      targetWindow,
    }: {
      ctx: CanvasRenderingContext2D;
      hostWindow: WindowState;
      targetWindow: WindowState;
    }) => {
      ctx.strokeStyle = "#ff0000";
      ctx.lineCap = "round";
      const currentWindowOffset: Coordinates = {
        x: hostWindow.screenX,
        y: hostWindow.screenY,
      };
      const targetWindowOffset: Coordinates = {
        x: targetWindow.screenX,
        y: targetWindow.screenY,
      };
 
      const origin = getWindowCenter(hostWindow);
      const target = getWindowCenter(targetWindow);
 
      const targetWithBaseChange = baseChange({
        currentWindowOffset,
        targetWindowOffset,
        targetPosition: target,
      });
 
      ctx.strokeStyle = "#ff0000";
      ctx.lineCap = "round";
      ctx.beginPath();
      ctx.moveTo(origin.x, origin.y);
      ctx.lineTo(targetWithBaseChange.x, targetWithBaseChange.y);
      ctx.stroke();
      ctx.closePath();
    };

現(xiàn)在,我們只需要對(duì)狀態(tài)變化做出響應(yīng)。

// main.ts
    sharedWorker.port.onmessage = (event: MessageEvent<WorkerMessage>) => {
        const msg = event.data;
        switch (msg.action) {
          case "sync": {
            const windows = msg.payload.allWindows;
            ctx.reset();
            drawMainCircle(ctx, center);
            windows
              .forEach(({ windowState: targetWindow }) => {
                drawConnectingLine({
                  ctx,
                  hostWindow: currentWindow,
                  targetWindow,
                });
              });
          }
        }
    };

最后一步,我們只需要定期檢查窗口是否更改,如果更改則發(fā)送消息即可。

setInterval(() => {
        const newWindow = getCurrentWindowState();
        if (
          didWindowChange({
            newWindow,
            oldWindow: currentWindow,
          })
        ) {
          sharedWorker.port.postMessage({
            action: "windowStateChanged",
            payload: {
              id,
              newWindow,
            },
          } satisfies WorkerMessage);
          currentWindow = newWindow;
        }
      }, 100);

實(shí)際上,我試驗(yàn)了很多次,以便更抽象更有科幻感,但總而言之要點(diǎn)都是一樣的。

如果一切順利,最后我們可以得到這樣炫酷的結(jié)果!

感謝閱讀!

責(zé)任編輯:武曉燕 來源: 前端新世界
相關(guān)推薦

2022-12-06 17:30:04

2017-11-27 12:24:02

命令行代碼指令

2015-08-12 16:32:34

華為/物聯(lián)網(wǎng)

2017-10-28 23:13:43

微服務(wù)架構(gòu)開發(fā)單體應(yīng)用

2016-12-02 20:43:28

Android

2014-11-25 15:02:01

客服系統(tǒng)

2017-11-06 19:09:45

在線抓娃娃機(jī)

2013-09-18 10:44:01

搜狗輸入法詞語

2020-11-04 07:36:06

Redis二進(jìn)制數(shù)據(jù)庫(kù)

2014-10-08 15:00:50

SUSE操作系統(tǒng)云計(jì)算

2023-12-11 13:57:00

RFM模型激勵(lì)機(jī)制

2020-12-28 08:36:30

C語言編程泛型

2010-08-02 13:55:20

2018-10-28 17:54:00

分布式事務(wù)數(shù)據(jù)

2016-03-21 11:09:52

Tableau/大數(shù)據(jù)

2013-05-28 09:35:14

驅(qū)動(dòng)之家

2021-04-19 05:42:51

Mmap文件系統(tǒng)

2022-08-02 16:37:32

服務(wù)網(wǎng)格網(wǎng)絡(luò)服務(wù)

2021-11-30 08:04:32

AIIT運(yùn)維
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 欧美日韩在线一区二区三区 | 一区二区三区在线免费观看 | 亚洲精品一区av在线播放 | 日韩一级精品视频在线观看 | 日韩欧美一区二区三区免费观看 | 国产一区免费 | 亚洲自拍偷拍视频 | 久久精品av麻豆的观看方式 | 国产亚洲精品精品国产亚洲综合 | 日韩成人免费av | 成人在线观看网站 | 中文字幕在线观看www | 亚洲午夜视频在线观看 | 在线欧美亚洲 | 一区二区三区影院 | 日本不卡视频 | 国产成人免费视频网站高清观看视频 | 特黄毛片 | 干干干操操操 | 国内精品伊人久久久久网站 | 一级a性色生活片久久毛片波多野 | 日韩av一二三区 | 国产欧美精品一区二区色综合朱莉 | 午夜在线观看视频 | 99久久久无码国产精品 | 美女在线一区二区 | 在线观看国产 | 免费av电影网站 | 欧美一区久久 | 成人深夜福利在线观看 | 久久久999成人 | 久草免费在线视频 | 中文字幕精品一区二区三区精品 | 综合精品 | 人人人干| 日韩午夜影院 | 亚洲精品播放 | 在线视频国产一区 | 国产福利二区 | 中文字幕一区二区三区精彩视频 | 精品免费国产视频 |