允许将项目拖动到窗口高度以下。普通 JavaScript

问题描述 投票:0回答:1

我正在尝试为我的客户构建一个网站,这与 Miro(或其他白板应用程序)没有什么不同。

这个概念是使用绝对定位来布局各个元素,但管理员可以将它们拖动到视口中并为每个元素设置宽度和高度属性。

我无法使用任何允许拖动到原始窗口高度以下的方法(即使整个文档的元素的高度设置大于窗口高度)。

我尝试了3种方法:

  1. 在包装器上设置一些大的高度属性。 https://jsfiddle.net/lharby/afjwsm75/

const draggingClass = 'dragging';

class draggable {
    constructor(element, node = [ document ] ) {
        this.node = node;
        this.el = element;

        if ( this.el ) {
            this.mouseDown = this.mouseDown.bind(this);
            this.mouseUp = this.mouseUp.bind(this);
            this.mouseMove = this.mouseMove.bind(this);
        }
    }

    setHandle( handle ) {
        this.handle = handle;
        return this;
    }

    setCallback( callback ) {
        this.callback = callback;
        return this;
    }

    mouseDown( event ) {
        if ( this.callback?.start ) this.callback.start( event, this.el )
        else this.el.classList.add(draggingClass)        
        for ( const node of this.node ) {
            node.addEventListener('mouseup', this.mouseUp);
            node.addEventListener('mousemove', this.mouseMove);
            node.addEventListener('mouseleave', this.mouseUp);
        }
        this.el.addEventListener('mouseleave', this.mouseUp);
    }

    mouseUp( event ) {
        if ( this.callback?.end ) this.callback.end( event, this.el )
        else this.el.classList.remove(draggingClass)
        for ( const node of this.node ) {
            node.removeEventListener('mouseup', this.mouseUp);
            node.removeEventListener('mousemove', this.mouseMove);
            node.removeEventListener('mouseleave', this.mouseUp);
        }
        this.el.removeEventListener('mouseleave', this.mouseUp);
    }

    mouseMove(event) {
        if ( this.callback?.move ) {
            this.callback.move( event, this.el );
        } else {
            const style = window.getComputedStyle(this.el);
            const x = (parseFloat(style.getPropertyValue('left')) || 0) + event.movementX;
            const y = (parseFloat(style.getPropertyValue('top')) || 0) + event.movementY;
            const rect = this.el.getBoundingClientRect();
            const viewHeight = window.innerHeight || document.documentElement.clientHeight;
            const viewWidth = window.innerWidth || document.documentElement.clientWidth;
            const maxX = viewWidth - rect.width;
            const maxY = viewHeight - rect.height;
            const constrainedX = Math.max(0, Math.min(x, maxX));
            const constrainedY = Math.max(0, Math.min(y, maxY));
            this.el.style.position = 'absolute';
            this.el.style.top = constrainedY + 'px';
            this.el.style.left = constrainedX + 'px';
        }
    }

    run() {
        const handle = this.handle ?? this.el
        handle.addEventListener('mousedown', this.mouseDown)
    }

    stop() {
        const handle = this.handle ?? this.el
        handle.removeEventListener('mousedown', this.mouseDown)
    }
}

const boxes = document.querySelectorAll('.box');
const movable1 = new draggable(boxes[0]);
const movable2 = new draggable(boxes[1]);
movable1.run();
movable2.run();
body {
    font-family: monospace;
}

.wrapper {
    min-height: 400vh;
    background-color: rgb(255 0 0 / 10%);
    top: 10px;
    right: 10px;
    bottom: 10px;
    left: 10px;
    overflow-y: scroll;
}

.box {
    position: absolute;
    padding: 10px;
    resize: both;
    height: 150px;
    width: 150px;
    margin-bottom: 20px;
    background: #f0f0f0;
    border: 2px solid #555;
    overflow: auto;
    background-image: linear-gradient(-45deg, #ccc 10px, transparent 10px);
}

.second {
    top: 50px;
    left: 50px;
}

.third {
    top: 100px;
    left: 100px;
}

.heading {
    background: red;
    padding: 5px 10px;
}

.dragging {
    opacity: 0.9;
    border: 2px dashed #8181b9;
    user-select: none;
    z-index: 2;
    cursor: move;
}

.actions {
    display: flex;
    flex-direction: column;
    position: absolute;
    right: 0;
    z-index: 1;
}
<div class="outer">
    <div class="wrapper">
        <div class="box">
            I am box 1
        </div>
        <div class="box second">
            I am box 2
        </div>

        <div class="box second third dragging">
            I am box 3
        </div>
    </div>
</div>

  1. 将 html 设置为固定位置,不滚动,并将框放置在允许大高度滚动的 div 内。 https://jsfiddle.net/lharby/25oqth67/

HTML 与上面相同,但使用此 CSS

html.no-scroll {
    overflow-y: hidden;
}

.outer {
    position: fixed;
    top: 0;
    bottom: 0;
    width: 100%;
}

.wrapper {
    height: 100%;
    background-color: rgb(255 0 0 / 10%);
    top: 10px;
    right: 10px;
    bottom: 10px;
    left: 10px;
    overflow-y: scroll;
}

.box {
    position: relative; // I also added a JS toggle to switch position from relative to absolute
    ...
}
  1. 动态尝试设置文档的高度或窗口高度。 https://jsfiddle.net/lharby/e8fb6537/

与前面的示例类似的 HTML 和 CSS,但这里有一些 JS

let vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);

const trigger = document.querySelector('.actions button');
trigger.addEventListener('click', () => {
    let root = document.querySelector(':root');
    let docHeight = document.documentElement.clientHeight;
    let winHeight = window.innerHeight;
    docHeight = 1200; // also tried `${1200}px`
    winHeight = 1300; // also tried `${1300}px`
    root.style.height = `${1400}px`;
    console.log(docHeight, winHeight, root.style.height);
    console.log(vh);
});

我还尝试在可拖动包装器下方添加一个元素。以及在 html 和 body 上设置高度属性。似乎没有什么能让用户将框拖到原始窗口高度以下。除非我错过了一些简单的 CSS 修复。

JSFiddles 包含按原样工作的所有其他功能(拖动框),但我在第一个示例中添加了一个代码片段,这将是最容易弄清楚的,因为我认为这可能是纯 CSS。

有趣的是,如果你用谷歌搜索这个主题,似乎有大量关于限制拖动到特定容器内的答案。

如果可能的话,我宁愿避开图书馆。

https://www.google.com/search?q=javascript+drag+item+beyond+window+height+site%3Astackoverflow.com

非常感谢任何帮助。

javascript css draggable
1个回答
0
投票

如果我正确理解您的问题,这就是您的代码中描述此行为的方式。

请看看我如何在代码中禁用约束并将 constrained... 替换为 x/y,现在一切正常:

        // const constrainedX = Math.max(0, Math.min(x, maxX));
        // const constrainedY = Math.max(0, Math.min(y, maxY));
        this.el.style.position = 'absolute';
        this.el.style.top = y + 'px';
        this.el.style.left = x + 'px';

如果这有帮助,请告诉我。

const draggingClass = 'dragging';

class draggable {
    constructor(element, node = [ document ] ) {
        this.node = node;
        this.el = element;

        if ( this.el ) {
            this.mouseDown = this.mouseDown.bind(this);
            this.mouseUp = this.mouseUp.bind(this);
            this.mouseMove = this.mouseMove.bind(this);
        }
    }

    setHandle( handle ) {
        this.handle = handle;
        return this;
    }

    setCallback( callback ) {
        this.callback = callback;
        return this;
    }

    mouseDown( event ) {
        if ( this.callback?.start ) this.callback.start( event, this.el )
        else this.el.classList.add(draggingClass)        
        for ( const node of this.node ) {
            node.addEventListener('mouseup', this.mouseUp);
            node.addEventListener('mousemove', this.mouseMove);
            node.addEventListener('mouseleave', this.mouseUp);
        }
        this.el.addEventListener('mouseleave', this.mouseUp);
    }

    mouseUp( event ) {
        if ( this.callback?.end ) this.callback.end( event, this.el )
        else this.el.classList.remove(draggingClass)
        for ( const node of this.node ) {
            node.removeEventListener('mouseup', this.mouseUp);
            node.removeEventListener('mousemove', this.mouseMove);
            node.removeEventListener('mouseleave', this.mouseUp);
        }
        this.el.removeEventListener('mouseleave', this.mouseUp);
    }

    mouseMove(event) {
        if ( this.callback?.move ) {
            this.callback.move( event, this.el );
        } else {
            const style = window.getComputedStyle(this.el);
            const x = (parseFloat(style.getPropertyValue('left')) || 0) + event.movementX;
            const y = (parseFloat(style.getPropertyValue('top')) || 0) + event.movementY;
            const rect = this.el.getBoundingClientRect();
            const viewHeight = window.innerHeight || document.documentElement.clientHeight;
            const viewWidth = window.innerWidth || document.documentElement.clientWidth;
            const maxX = viewWidth - rect.width;
            const maxY = viewHeight - rect.height;
            // const constrainedX = Math.max(0, Math.min(x, maxX));
            // const constrainedY = Math.max(0, Math.min(y, maxY));
            this.el.style.position = 'absolute';
            this.el.style.top = y + 'px';
            this.el.style.left = x + 'px';
        }
    }

    run() {
        const handle = this.handle ?? this.el
        handle.addEventListener('mousedown', this.mouseDown)
    }

    stop() {
        const handle = this.handle ?? this.el
        handle.removeEventListener('mousedown', this.mouseDown)
    }
}

const boxes = document.querySelectorAll('.box');
const movable1 = new draggable(boxes[0]);
const movable2 = new draggable(boxes[1]);
movable1.run();
movable2.run();
body {
    font-family: monospace;
}

.wrapper {
    min-height: 400vh;
    background-color: rgb(255 0 0 / 10%);
    top: 10px;
    right: 10px;
    bottom: 10px;
    left: 10px;
    overflow-y: scroll;
}

.box {
    position: absolute;
    padding: 10px;
    resize: both;
    height: 150px;
    width: 150px;
    margin-bottom: 20px;
    background: #f0f0f0;
    border: 2px solid #555;
    overflow: auto;
    background-image: linear-gradient(-45deg, #ccc 10px, transparent 10px);
}

.second {
    top: 50px;
    left: 50px;
}

.third {
    top: 100px;
    left: 100px;
}

.heading {
    background: red;
    padding: 5px 10px;
}

.dragging {
    opacity: 0.9;
    border: 2px dashed #8181b9;
    user-select: none;
    z-index: 2;
    cursor: move;
}

.actions {
    display: flex;
    flex-direction: column;
    position: absolute;
    right: 0;
    z-index: 1;
}
<div class="outer">
    <div class="wrapper">
        <div class="box">
            I am box 1
        </div>
        <div class="box second">
            I am box 2
        </div>

        <div class="box second third dragging">
            I am box 3
        </div>
    </div>
</div>

© www.soinside.com 2019 - 2024. All rights reserved.