两个跨域页面实现量子纠缠的终极方案

本文约定:

A页面:假设为a.com

B页面:假设为b.com

一、同域页面实现量子纠缠的一般方案

最近前端圈的量子纠缠交互动效爆火,相信大家都了解了其中的实现原理,这里我再简单的介绍下,不了解的同学的可以学习一波。

原理就是,通过监听窗口的位置、大小等信息,然后实时地保存到localStorage中,另一个页面通过监听strorage的改变,进而动态地改变页面上物体的位置和显示效果。

示例代码如下:

javascript 复制代码
// 将当前页面的中心位置坐标保存到localStorage中
const savePageInfo = () => {
    const centerX = window.innerWidth / 2 + window.screenLeft;
    const centerY = window.innerHeight / 2 + window.screenTop;
    const data = { centerX, centerY };
    window.localStorage.setItem(pageId,  JSON.stringify(data));
};

// localStorage被修改后的回调
const onStorage = (e) => {
    console.log("otherPageInfo", JSON.parse(e.newValue));
}

const onReady = () => {
    savePageInfo();
    window.addEventListener("resize", savePageInfo);
    window.addEventListener("storage", onStorage);
}

window.addEventListener("ready", onReady);

在这个案例中,由于两个页面是相同域下的,所以另外一个页面可以通过监听strorage的改变,获取到前一个页面保存进去的值,然后进行交互,这里不得不佩服作者的奇思妙想。

那么问题来了,如果两个页面是跨域的,单独打开的两个页面又如何进行实时地通信呢?有同学会说用WebSocket,呃...,是可以实现,但我们讨论的是纯前端的实现,借助后端就没啥意思了。

二、跨域页面实现量子纠缠的终极方案

两个跨域页面想要通信就必须使用可以进行跨域通信的API,大家肯定都想到了,对,使用window.postMessage,但是A页面要给B页面发送消息,那么postMessage的window对象就必须是B页面的window对象,而A和B两个页面是在两个单独的浏览器窗口中分别打开的,A页面如何获取到B页面的window对象呢?

直接说结论,终极方案就是,在A页面中通过Iframe嵌入display为none的B页面,A页面监听窗口的位置、大小的改变,然后将窗口信息通过postMessage发送消息给B页面,刚才说了要给B页面发送消息就必须拿到B页面的window对象,那么如何拿到呢,就是通过Iframe.contentWindow,B页面通过监听A页面发送的message拿到信息,然后保存到window.localStorage中。这里有同学会好奇了,既然B页面拿到了信息,为啥还要保存到localStorage中,为啥不直接使用呢?你听我狡辩...,啊不,你听我说,这里拿到信息的B页面只是A页面中嵌入的B页面,不是用户打开的另一个窗口中的B页面,所以这时剩下的工作就是将嵌入的B页面中的信息发送给另一个窗口的B页面,就大功告成了。两个都是B页面,就不存在跨域啦,就使用上面那个方案,通过监听strorage的改变获取就行了。

示例代码如下:

A页面:
ini 复制代码
const postInfo = () => {
    const bIframe = document.getElementById("bIframe");
    if (bIframe) {
        const centerX = window.innerWidth / 2 + window.screenLeft;
        const centerY = window.innerHeight / 2 + window.screenTop;
        const data = { centerX, centerY  };
        bIframe.contentWindow?.postMessage(
            JSON.stringify(data),
            "http://b.com"
        );
    }
};

const onMessage = (e) => {
    if (e.origin !== "http://b.com") return;
    if (e.data) {
        window.localStorage.setItem("bPageInfo", e.data);
    }
};

const onStorage = (e) => {
    if (e.key === "bPageInfo") {
        console.log("bPageInfo", JSON.parse(e.newValue));
    }
};

const onReady = () => {
    window.addEventListener("message", onMessage, false);
    if (window.self === window.top) {
        window.addEventListener("storage", onStorage);
        window.addEventListener("resize", postInfo);
    }
    postInfo();
}

window.addEventListener("ready", onReady);

在A页面中嵌入B页面

ini 复制代码
<iframe
    id="bIframe"
    src="http://b.com"
    style="display: none"
/>
B页面:
ini 复制代码
const postInfo = () => {
    const aIframe = document.getElementById("aIframe");
    if (aIframe) {
        const centerX = window.innerWidth / 2 + window.screenLeft;
        const centerY = window.innerHeight / 2 + window.screenTop;
        const data = { centerX, centerY  };
        aIframe.contentWindow?.postMessage(
            JSON.stringify(data),
            "http://a.com"
        );
    }
};

const onMessage = (e) => {
    if (e.origin !== "http://a.com") return;
    if (e.data) {
        window.localStorage.setItem("aPageInfo", e.data);
    }
};

const onStorage = (e) => {
    if (e.key === "aPageInfo") {
        console.log("aPageInfo", JSON.parse(e.newValue));
    }
};

const onReady = () => {
    window.addEventListener("message", onMessage, false);
    if (window.self === window.top) {
        window.addEventListener("storage", onStorage);
        window.addEventListener("resize", postInfo);
    }
    postInfo();
}

window.addEventListener("ready", onReady);

三、实战案例

Visualization Collection网站中已经实现了量子纠缠的效果,大家可以去玩一玩,该网站中的量子纠缠效果作为隐藏款案例,点击网站左上角头部的文字,即可触发,一般我不说的。

体验地址(PC端):hepengwei.cn

源码地址:github.com/hepengwei/v...

四、美中不足

大家也看到了,上面GIF图中演示的还是同域页面实现量子纠缠的效果,这里非常遗憾地告诉大家,跨域页面实现量子纠缠只能在本地运行的情况下才可实现,部署到线上就不行了,监听storage的回调不会执行,后面我也试过各种办法,比如在定时器里循环去获取localStorage中的值,使用Service Worker等均无法实现,这应该是浏览器为了安全考虑进行了限制。

如果有想要看两个跨域页面在本地实现量子纠缠效果的同学,可以自行到我GitHub上下载visualization-collection和michelle-design这两个项目代码,然后在本地运行,就可以看到效果啦。

五、结语

该终极方案与我的另一篇文章《两个跨越页面进行跳转传参的终极方案》中的终极方案有异曲同工之妙,都是通过在原先的页面中无痕嵌入另一个页面,然后通过myIframe.contentWindow.postMessage来发送消息,如此实现跨域信息的传递。两个不同窗口打开的同域页面,则通过将信息保存到localStorage,另一页面通过监听storage改变的方式传递信息。

如果文章有哪些不对的地方或者有其他更好的方案,欢迎大家留言讨论。

更多个人文章

  1. 将React项目代码转成Vue3时最容易踩坑的地方
  2. 实现球体碰撞,使用这个库就够了
  3. 全网最全Autoit3基础教程及实战案例
  4. 面试秘籍之手写系列
  5. 十分钟带你入门Chrome插件开发
相关推荐
周亚鑫9 分钟前
vue3 pdf base64转成文件流打开
前端·javascript·pdf
落魄小二18 分钟前
el-table 表格索引不展示问题
javascript·vue.js·elementui
y52364819 分钟前
Javascript监控元素样式变化
开发语言·javascript·ecmascript
fruge33 分钟前
纯css制作声波扩散动画、js+css3波纹催眠动画特效、【css3动画】圆波扩散效果、雷达光波效果完整代码
javascript·css·css3
neter.asia42 分钟前
vue中如何关闭eslint检测?
前端·javascript·vue.js
嚣张农民1 小时前
JavaScript中Promise分别有哪些函数?
前端·javascript·面试
光影少年1 小时前
vue2与vue3的全局通信插件,如何实现自定义的插件
前端·javascript·vue.js
Rattenking1 小时前
React 源码学习01 ---- React.Children.map 的实现与应用
javascript·学习·react.js
熊的猫2 小时前
JS 中的类型 & 类型判断 & 类型转换
前端·javascript·vue.js·chrome·react.js·前端框架·node.js
别拿曾经看以后~4 小时前
【el-form】记一例好用的el-input输入框回车调接口和el-button按钮防重点击
javascript·vue.js·elementui