My Personal Blog

Published on

实现多窗口拖拽卡片的示例

Authors
  • avatar
    Name
    Tian Haipeng

1. HTML 结构

我们首先创建一个简单的 HTML 页面,其中包含一张图片作为拖拽卡片。

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Multi-window Drag Card</title>
    <style>
        .card {
            width: 200px;
            height: 400px;
            position: absolute;
        }
    </style>
</head>
<body>

<img class="card" src="./Q.jpg" alt=""/>

<script>
</script>

</body>
</html>

2. 初始化图片源

我们可以从 URL 参数中获取图片类型,并相应地设置卡片的图片源。

function init() {
    const url = new URL(location.href);
    const type = url.searchParams.get('type') || 'Q';
    card.src = `./${type}.jpg`;
}

init();

3. 禁用默认拖拽行为

为了防止图片被浏览器默认处理为拖拽对象,我们需要禁用图片的默认拖拽行为。

const card = document.querySelector('.card');
// 阻止图片的默认拖拽行为
card.ondragstart = (e) => {
    e.preventDefault();
}

4. 实现拖拽功能

我们需要实现拖拽的逻辑,使得卡片能够跟随鼠标移动。同时,我们还要将卡片的新位置发送到其他窗口。

card.onmousedown = (e) => {
    let x = e.pageX - card.offsetLeft;
    let y = e.pageY - card.offsetTop;
    window.onmousemove = function (e) {
        const cx = e.pageX - x;
        const cy = e.pageY - y;
        card.style.left = cx + 'px';
        card.style.top = cy + 'px';
        const points = clientToScreen(cx, cy);
        channel.postMessage(points);
    }
    window.onmouseup = function () {
        window.onmousemove = null;
        window.onmouseup = null;
    }
}

5. 计算屏幕位置

为了在不同的窗口之间同步拖拽位置,我们需要将鼠标的位置从客户端坐标转换为屏幕坐标,并且反向转换。这是因为浏览器的 screenXscreenY 属性提供了相对于屏幕的坐标,而 clientXclientY 提供了相对于客户端的坐标。

function barHeight() {
    return window.outerHeight - window.innerHeight;
}

function clientToScreen(clientX, clientY) {
    const screenX = clientX + window.screenX;
    const screenY = clientY + window.screenY + barHeight();
    return [screenX, screenY];
}

function screenToClient(screenX, screenY) {
    const clientX = screenX - window.screenX;
    const clientY = screenY - window.screenY - barHeight();
    return [clientX, clientY];
}

6. 使用 BroadcastChannel 实现跨窗口通信

BroadcastChannel API 允许我们在多个窗口之间发送和接收消息。我们将使用它来同步拖拽卡片的位置。

const channel = new BroadcastChannel('card');
channel.onmessage = (e) => {
    console.log(e.data);
    
    const [x, y] = screenToClient(...e.data);
    card.style.left = x + 'px';
    card.style.top = y + 'px';
}

总结

通过以上步骤,就成功实现了一个多窗口拖拽卡片的示例。这个例子展示了如何使用 BroadcastChannel API 在多个窗口之间同步拖拽操作,并使用 JavaScript 处理鼠标事件和坐标转换。

以下是完整的代码:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Multi-window Drag Card</title>
    <style>
        .card {
            width: 200px;
            height: 400px;
            position: absolute;
        }
    </style>
</head>
<body>

<img class="card" src="./Q.jpg" alt=""/>

<script>
    const card = document.querySelector('.card');
      // 阻止图片的默认拖拽行为
      card.ondragstart = (e) => {
        e.preventDefault();
    }

    function barHeight() {
        return window.outerHeight - window.innerHeight;
    }

    function clientToScreen(clientX, clientY) {
        const screenX = clientX + window.screenX;
        const screenY = clientY + window.screenY + barHeight();
        return [screenX, screenY];

    }

    function screenToClient(screenX, screenY) {
        const clientX = screenX - window.screenX;
        const clientY = screenY - window.screenY - barHeight();
        return [clientX, clientY];
    }

    const channel = new BroadcastChannel('card');
    channel.onmessage = (e) => {
        console.log(e.data);
        
        const [x, y] = screenToClient(...e.data);
        card.style.left = x + 'px';
        card.style.top = y + 'px';
    }

    card.onmousedown = (e) => {
        let x = e.pageX - card.offsetLeft;
        let y = e.pageY - card.offsetTop;
        window.onmousemove = function (e) {
            const cx = e.pageX - x;
            const cy = e.pageY - y;
            card.style.left = cx + 'px';
            card.style.top = cy + 'px';
            const points = clientToScreen(cx, cy);
            channel.postMessage(points);
        }
        window.onmouseup = function () {
            window.onmousemove = null;
            window.onmouseup = null;
        }
    }

    function init() {
        const url = new URL(location.href);
        const type = url.searchParams.get('type') || 'Q';
        card.src = `./${type}.jpg`;
    }

    init();
</script>

</body>
</html>