我们通过一个实战例子来学习屏幕坐标、窗口通信

效果图test.gif

  • 思考一个问题,上述效果图是不是多个窗口公用一个元素?

    • 显然这是不可能的,肯定是三个元素。
    • 同一个页面通过url传参的方式根据参数不同,给元素设置不同的背景。
  • 做到上述效果图需要哪些必要条件?

    • 保证元素的位置在同一个坐标系下相同。
    • 只有在电脑屏幕下,它们的坐标系才是相同的。
  • 涉及到的知识点

    • 导航栏的高度计算

         //获取浏览器窗口的工具栏、菜单栏和边框等元素的总高度
         function barHeight() {
             return window.outerHeight - window.innerHeight;
         }
    • 屏幕坐标和视口坐标的转换
      解析图x.jpg

         //将视口坐标转换为屏幕坐标
         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];
         }
    • 拖拽事件

        // 选择类名为.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;
        }
        }
      
    • 窗口之间的通信

        // 创建一个名为'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();
Last modification:September 6, 2024
如果觉得我的文章对你有用,请随意赞赏