我们通过一个实战例子来学习屏幕坐标、窗口通信
效果图
思考一个问题,上述效果图是不是多个窗口公用一个元素?
- 显然这是不可能的,肯定是三个元素。
- 同一个页面通过url传参的方式根据参数不同,给元素设置不同的背景。
做到上述效果图需要哪些必要条件?
- 保证元素的位置在同一个坐标系下相同。
- 只有在电脑屏幕下,它们的坐标系才是相同的。
涉及到的知识点
导航栏的高度计算
代码语言:javascript复制 //获取浏览器窗口的工具栏、菜单栏和边框等元素的总高度
function barHeight() {
return window.outerHeight - window.innerHeight;
}
屏幕坐标和视口坐标的转换 解析图
代码语言:javascript复制 //将视口坐标转换为屏幕坐标
function clientToScreen(clientX, clientY) {
//clientX, clientY 是视口坐标
//window.screenX 、window.screenY 是浏览器窗口左上角相对于屏幕左上角的距离
let screenX = clientX window.screenX;
//注意:window.screenY 是浏览器窗口左上角相对于屏幕左上角的距离,不包括浏览器窗口的工具栏、菜单栏和边框等元素的总高度
let screenY = clientY window.screenY barHeight();
return [screenX, screenY];
}
//将屏幕坐标转换为视口坐标
function screenToClient(screenX, screenY) {
let clientX = screenX - window.screenX;
let clientY = screenY - window.screenY - barHeight();
return [clientX, clientY];
}
拖拽事件
代码语言:javascript复制 // 选择类名为.container 的元素
const card = document.querySelector('.container');
// 为选中的元素添加鼠标按下事件监听器
card.onmousedown = function (e) {
// 计算鼠标指针在卡片元素上的相对偏移量
const x = e.pageX - card.offsetLeft;
const y = e.pageY - card.offsetTop;
window.onmousemove = function (e) {
// 根据相对偏移量和鼠标移动事件的坐标计算卡片新的位置
let cx = e.pageX - x;
let cy = e.pageY - y;
// 设置卡片元素的新位置
card.style.left = cx "px";
card.style.top = cy "px";
}
window.onmouseup = function () {
// 释放鼠标时,取消鼠标移动和鼠标释放的事件监听器
window.onmousemove = null;
window.onmouseup = null;
}
}
窗口之间的通信
代码语言:javascript复制 // 创建一个名为'card'的广播频道,这个频道允许不同窗口或标签之间的脚本进行通信
const channel = new BroadcastChannel('card');
// 将当前窗口元素位置的坐标转换为屏幕坐标
let points = clientToScreen(cx, cy);
// 向其他窗口传递当前窗口元素位置的屏幕坐标
channel.postMessage(points);
// 监听其他窗口传递过来的消息
channel.onmessage = function (event) {
// 使用展开运算符将事件数据转换为当前可视坐标
let [clientX, clientY] = screenToClient(...event.data);
// 设置元素的left属性值,将视口的X坐标应用于card元素
card.style.left = clientX "px";
// 设置元素的top属性值,将视口的Y坐标应用于card元素
card.style.top = clientY "px";
完整代码
- html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>屏幕坐标和窗口通信</title>
<link rel="stylesheet" href="static/css/reset.css">
<link rel="stylesheet" href="static/css/index.css">
</head>
<body>
<div class="container"></div>
<script src="static/js/index.js"></script>
</body>
</html>
- css
.container{
width:300px;
height: 300px;
position: absolute;
}
- js
//获取浏览器窗口的工具栏、菜单栏和边框等元素的总高度
function barHeight() {
return window.outerHeight - window.innerHeight;
}
//将视口坐标转换为屏幕坐标
function clientToScreen(clientX, clientY) {
let screenX = clientX window.screenX;
let screenY = clientY window.screenY barHeight();
return [screenX, screenY];
}
//将屏幕坐标转换为视口坐标
function screenToClient(screenX, screenY) {
let clientX = screenX - window.screenX;
let clientY = screenY - window.screenY - barHeight();
return [clientX, clientY];
}
//创建一个名为'card'的广播频道,这个频道允许不同窗口或标签之间的脚本进行通信
const channel = new BroadcastChannel('card');
// 监听'card'频道上的消息事件
channel.onmessage = function (event) {
// 使用展开运算符将事件数据转换为视口坐标
let [clientX, clientY] = screenToClient(...event.data);
// 设置元素的left属性值,将视口的X坐标应用于card元素
card.style.left = clientX "px";
// 设置元素的top属性值,将视口的Y坐标应用于card元素
card.style.top = clientY "px";
}
// 选择类名为.container 的元素
const card = document.querySelector('.container');
// 为选中的元素添加鼠标按下事件监听器
card.onmousedown = function (e) {
// 计算鼠标指针在卡片元素上的相对偏移量
const x = e.pageX - card.offsetLeft;
const y = e.pageY - card.offsetTop;
window.onmousemove = function (e) {
// 根据相对偏移量和鼠标移动事件的坐标计算卡片新的位置
let cx = e.pageX - x;
let cy = e.pageY - y;
// 设置卡片元素的新位置
card.style.left = cx "px";
card.style.top = cy "px";
// 将卡片当前位置的坐标转换为屏幕坐标
let points = clientToScreen(cx, cy);
// 通过某个数据通道发送屏幕坐标信息
channel.postMessage(points);
}
window.onmouseup = function () {
// 释放鼠标时,取消鼠标移动和鼠标释放的事件监听器
window.onmousemove = null;
window.onmouseup = null;
}
}
// 定义初始化函数 init
function init() {
// 创建一个 URL 对象,用于解析当前窗口的 URL
let url = new URL(window.location.href);
// 使用 URL 对象的 searchParams 属性获取 URL 中的查询字符串参数 type
let type = url.searchParams.get("type") || "a";
// 判断 type 是否等于 a,如果是,将 card 元素的背景颜色设置为红色
if (type === "a") {
card.style.background = "red";
}
// 判断 type 是否等于 b,如果是,将 card 元素的背景颜色设置为绿色
else if (type === "b") {
card.style.background = "green";
}
// 判断 type 是否等于 c,如果是,将 card 元素的背景颜色设置为蓝色
else if (type === "c") {
card.style.background = "blue";
}
}
// 调用 init 函数,启动初始化逻辑
init();