一、前言
最近 推特上 一位懂设计和写代码的大神一个两个浏览器之间 星球粒子交互的动画火了, 让人看了大呼脑洞大开, 浏览器竟然还能这么玩!!!
准备自己也搞搞玩一下
二、实现
其实实现类似的功能需要的难点并不多,不在乎以下几个步骤
- 1、 屏幕坐标和窗口坐标转换
- 2、 跨标签通讯
1、 先来看第一个点, 获取屏幕坐标与窗口坐标
js
// 屏幕坐标转换为窗口坐标
const screenToClient = (screenX, screenY) => {
const clienX = screenX - window.screenX;
const clienY = screenY - window.screenY - barHeight();
return [clienX, clienY];
};
// 窗口坐标转换为屏幕坐标
const clientToScreen = (clienX, clienY) => {
const screenX = clienX + window.screenX;
const screenY = clienY + window.screenY + barHeight();
return [screenX, screenY];
};
我们先简单实现一个卡片, 通过url上面传递颜色值, 设置定位
在卡片本上设置上点击拖动等事件
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>跨标签通讯</title>
</head>
<style>
.card {
width: 300px;
height: 300px;
background-color: #f00;
position: fixed;
top: 100px;
left: 100px;
}
</style>
<body>
跨标签通讯
<div class="card">card</div>
</body>
<script>
const barHeight = () => window.outerHeight - window.innerHeight;
const cardDom = document.querySelector(".card");
cardDom.style.top = 100 + "px";
cardDom.style.left = 100 + "px";
cardDom.style.background =
new URLSearchParams(window.location.search).get("color") || "red";
window.onload = function () {
cardDom.onmousedown = function (e) {
cardDom.style.cursor = "pointer";
let x = e.pageX - cardDom.offsetLeft;
let y = e.pageY - cardDom.offsetTop;
window.onmousemove = function (e) {
cardDom.style.left = e.clientX - x + "px";
cardDom.style.top = e.clientY - y + "px";
// 发送消息
const clientCoordinateX = e.clientX - x;
const clientCoordinateY = e.clientY - y;
const ScreenCoordinate = clientToScreen(
clientCoordinateX,
clientCoordinateY
);
sendMessage(ScreenCoordinate);
};
window.onmouseup = function () {
window.onmousemove = null;
window.onmouseup = null;
cardDom.style.cursor = "unset";
};
};
};
</script>
</html>
单个元素的拖动就实现了, 很简单, 如何让其他标签的元素也能同步进行, 需要实现跨标签方案了, 可以参考该文章- 跨标签页通信的8种方式
我们就选择第一种,使用 BroadCast Channel, 使用也很简单
js
// 创建 Broadcast Channel
const channel = new BroadcastChannel("myChannel");
// 监听消息
channel.onmessage = (event) => {
// 处理接收到的消息
console.log('接收',event)
};
// 发送消息
const sendMessage = (message) => {
channel.postMessage(message);
};
只需要在移动时发送消息, 再其他标签页就可以接收到值了, 现在关键的就是收到发送的坐标点后, 如何处理, 其实关键就是要让几个窗口的卡片位置转化到同一个纬度, 让其再超出浏览器的时候,再另一个窗口的同一个位置出现, 所以就需要将窗口坐标转化成屏幕坐标,发送给其他窗口后, 再转化成窗口坐标进行渲染即可
完整代码实现如下
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>跨标签通讯</title>
</head>
<style>
.card {
width: 300px;
height: 300px;
background-color: #f00;
position: fixed;
top: 100px;
left: 100px;
}
</style>
<body>
跨标签通讯
<div class="card">card</div>
</body>
<script>
const barHeight = () => window.outerHeight - window.innerHeight;
const cardDom = document.querySelector(".card");
cardDom.style.top = 100 + "px";
cardDom.style.left = 100 + "px";
cardDom.style.background =
new URLSearchParams(window.location.search).get("color") || "red";
// 屏幕坐标转换为窗口坐标
const screenToClient = (screenX, screenY) => {
const clienX = screenX - window.screenX;
const clienY = screenY - window.screenY - barHeight();
return [clienX, clienY];
};
// 窗口坐标转换为屏幕坐标
const clientToScreen = (clienX, clienY) => {
const screenX = clienX + window.screenX;
const screenY = clienY + window.screenY + barHeight();
return [screenX, screenY];
};
// 创建 Broadcast Channel
const channel = new BroadcastChannel("myChannel");
// 监听消息
channel.onmessage = (event) => {
// 处理接收到的消息
const [clienX, clienY] = screenToClient(...event.data);
// 不同窗口的卡片要在同一个位置, 要放到同一个坐标系下面,保持屏幕坐标一致
cardDom.style.left = clienX + "px";
cardDom.style.top = clienY + "px";
};
// 发送消息
const sendMessage = (message) => {
channel.postMessage(message);
};
window.onload = function () {
cardDom.onmousedown = function (e) {
cardDom.style.cursor = "pointer";
let x = e.pageX - cardDom.offsetLeft;
let y = e.pageY - cardDom.offsetTop;
window.onmousemove = function (e) {
cardDom.style.left = e.clientX - x + "px";
cardDom.style.top = e.clientY - y + "px";
// 发送消息
const clientCoordinateX = e.clientX - x;
const clientCoordinateY = e.clientY - y;
const ScreenCoordinate = clientToScreen(
clientCoordinateX,
clientCoordinateY
);
sendMessage(ScreenCoordinate);
};
window.onmouseup = function () {
window.onmousemove = null;
window.onmouseup = null;
cardDom.style.cursor = "unset";
};
};
};
</script>
</html>