本文约定:
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改变的方式传递信息。
如果文章有哪些不对的地方或者有其他更好的方案,欢迎大家留言讨论。
更多个人文章