如何实现从一个div中拖拽dom到另一个div容器?

页面布局如下:

画布区域通过panzoom实现了放、缩、拖拽等基础功能,里面放置了一些dom节点,绝对定位,都有width、height、left、top属性,节点已经实现了移动功能,但是现在要求右侧有一个待操作面板,希望把右侧容器里面的dom拖拽到画布鼠标松开的位置,目前主要的困难是:点击右侧待操作区域节点时,如何计算该节点对应到画布区域的坐标(x、y)?因为画布区域是可以拖拽、放、缩的。

阅读 555
2 个回答

1736233878427.png

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>拖拽Demo</title>
    <style>
        .container {
            display: flex;
            height: 100vh;
            margin: 0;
            padding: 20px;
            box-sizing: border-box;
        }

        .top-panel {
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            height: 50px;
            border-bottom: 1px solid #ccc;
        }

        .left-panel {
            width: 200px;
            border-right: 1px solid #ccc;
        }

        .canvas-container {
            flex: 1;
            position: relative;
            overflow: hidden;
            background: #f5f5f5;
            margin: 0 20px;
        }

        .canvas {
            position: absolute;
            width: 100%;
            height: 100%;
            transform-origin: 0 0;
        }

        .right-panel {
            width: 200px;
            border-left: 1px solid #ccc;
            padding: 10px;
        }

        .draggable-item {
            width: 100px;
            height: 50px;
            background: #fff;
            border: 1px solid #ddd;
            margin: 10px 0;
            cursor: move;
            user-select: none;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .canvas-item {
            position: absolute;
            background: #fff;
            border: 1px solid #ddd;
            cursor: move;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="left-panel"></div>
        <div class="canvas-container">
            <div id="canvas" class="canvas"></div>
        </div>
        <div class="right-panel">
            <div class="draggable-item" draggable="true" data-type="item1">组件1</div>
            <div class="draggable-item" draggable="true" data-type="item2">组件2</div>
            <div class="draggable-item" draggable="true" data-type="item3">组件3</div>
        </div>
    </div>

    <script src="https://unpkg.com/panzoom@9.4.0/dist/panzoom.min.js"></script>
    <script>
        const canvas = document.getElementById('canvas');
        const panzoomInstance = panzoom(canvas, {
            maxZoom: 2,
            minZoom: 0.5,
            bounds: true,
            boundsPadding: 0.1
        });

        const canvasContainer = document.querySelector('.canvas-container');

        document.querySelectorAll('.draggable-item').forEach(item => {
            item.addEventListener('dragstart', (e) => {
                const rect = e.target.getBoundingClientRect();
                const offsetX = e.clientX - rect.left;
                const offsetY = e.clientY - rect.top;
                
                e.dataTransfer.setData('text/plain', JSON.stringify({
                    type: e.target.dataset.type,
                    offsetX,
                    offsetY
                }));
                e.dataTransfer.effectAllowed = 'copy';
            });
        });

        canvas.addEventListener('dragover', (e) => {
            e.preventDefault();
            e.dataTransfer.dropEffect = 'copy';
        });

        canvas.addEventListener('drop', (e) => {
            e.preventDefault();
            const data = JSON.parse(e.dataTransfer.getData('text/plain'));
            
            const transform = panzoomInstance.getTransform();
            const rect = canvasContainer.getBoundingClientRect();

            // 计算放置位置时考虑缩放比例
            const x = (e.clientX - rect.left - transform.x - (data.offsetX * transform.scale)) / transform.scale;
            const y = (e.clientY - rect.top - transform.y - (data.offsetY * transform.scale)) / transform.scale;

            const newItem = document.createElement('div');
            newItem.className = 'canvas-item';
            newItem.style.width = '100px';
            newItem.style.height = '50px';
            newItem.style.left = `${x}px`;
            newItem.style.top = `${y}px`;
            newItem.textContent = data.type;

            makeDraggable(newItem);
            canvas.appendChild(newItem);
        });

        function makeDraggable(element) {
            let initialX, initialY;
            let startX, startY;
            
            element.onmousedown = dragMouseDown;

            function dragMouseDown(e) {
                e.preventDefault();
                e.stopPropagation();

                const transform = panzoomInstance.getTransform();
                
                // 记录元素初始位置
                initialX = parseFloat(element.style.left);
                initialY = parseFloat(element.style.top);
                
                // 记录鼠标初始位置(考虑缩放和平移)
                startX = (e.clientX - canvasContainer.getBoundingClientRect().left - transform.x) / transform.scale;
                startY = (e.clientY - canvasContainer.getBoundingClientRect().top - transform.y) / transform.scale;
                
                document.onmousemove = elementDrag;
                document.onmouseup = closeDragElement;
                panzoomInstance.pause();
            }

            function elementDrag(e) {
                e.preventDefault();
                
                const transform = panzoomInstance.getTransform();
                
                // 计算当前鼠标位置(考虑缩放和平移)
                const currentX = (e.clientX - canvasContainer.getBoundingClientRect().left - transform.x) / transform.scale;
                const currentY = (e.clientY - canvasContainer.getBoundingClientRect().top - transform.y) / transform.scale;
                
                // 计算位移
                const deltaX = currentX - startX;
                const deltaY = currentY - startY;
                
                // 更新元素位置
                element.style.left = `${initialX + deltaX}px`;
                element.style.top = `${initialY + deltaY}px`;
            }

            function closeDragElement() {
                document.onmousemove = null;
                document.onmouseup = null;
                panzoomInstance.resume();
            }
        }
    </script>
</body>
</html>

应该可以:

const { scale, x, y } = panzoomInstance.getTransform();
const matrix = new DOMMatrix()
.scale(scale, scale) // 缩放
.translate(x, y) // 平移
.inverse(); // 求逆
const { x: originX, y: originY } = matrix.transformPoint({
  x: offsetX,
  y: offsetY
})

// offsetX, offsetY 为鼠标相对于变换原点的坐标
// originX, originY 为鼠标映射到画布上的点的坐标
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏