HTML块拖拽交换

拖拽交换

效果图如下

静态效果图

动态效果图

CSS样式

css 复制代码
.container {
    background-color: #d4d4d4;
    /* 修改背景色 */
    border: 2px solid black;
    height: 320px;
    width: 240px;
    position: relative;
    overflow: hidden;
    /* 防止内容溢出 */
}

.block {
    position: absolute;
    width: 50px;
    height: 32px;
    border: 2px solid black;
    border-radius: 5px;
    text-align: center;
    font-weight: bolder;
    cursor: move;
    user-select: none;
    box-sizing: border-box;
    /* 确保边框不增加元素尺寸 */
}

.filled {
    background-color: #00ff00;
}

.empty {
    background-color: #ffffff;
}

.dragging {
    opacity: 0.8;
    z-index: 100;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}

.title {
    font-size: 20px;
    color: red;
    margin-bottom: 10px;
}

HTML页面

html 复制代码
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>拖拽交换</title>
	</head>
	<body>
		<div id="app">
			<div class="title">拖拽交换1</div>
			<div class="container" id="dropzone">
				<div v-for="(block, index) in blocks" :key="index" :id="'block-'+index"
					:class="['block', block.content ? 'filled' : 'empty']"
					:style="{ top: block.top + 'px', left: block.left + 'px' }">
					{{ block.content }}
				</div>
			</div>
		</div>

        <!-- 引入拖拽库  -->
		<script src="js/interact.js"></script> 
        <!-- 引入Vue库  -->
		<script src="js/vue.min.js"></script>
        
	</body>
</html>

JavaScript

js 复制代码
<script>
    let app = new Vue({
        el: '#app',
        data: {
            blocks: [],
            blockSize: {
                width: 45,
                height: 25
            },
            margin: {
                x: 5,
                y: 10
            },
            cols: 4,
            containerRect: null
        },
        created() {},
        mounted() {
            this.initBlocks();
            this.$nextTick(() => {
                this.containerRect = document.getElementById('dropzone').getBoundingClientRect();
                this.setupDragAndDrop();
            });
        },
        methods: {
            // 初始化块及其区域
            initBlocks() {
                const blocks = [];
                const blockWidth = this.blockSize.width + this.margin.x * 2;
                const blockHeight = this.blockSize.height + this.margin.y * 2;

                for (let i = 0; i < 24; i++) {
                    const row = Math.floor(i / this.cols);
                    const col = i % this.cols;

                    let cont = '';
                    if (i === 0) cont = '3';
                    if (i == 13) cont = '127';

                    blocks.push({
                        id: i,
                        content: cont,
                        top: row * blockHeight + this.margin.y,
                        left: col * blockWidth + this.margin.x,
                        originalTop: row * blockHeight + this.margin.y,
                        originalLeft: col * blockWidth + this.margin.x
                    });
                }
                this.blocks = blocks;
            },

            // 设置拖拽与交换
            setupDragAndDrop() {
                const self = this;
                const container = document.getElementById('dropzone');

                interact('.block.filled').draggable({
                    inertia: false,
                    autoScroll: false,
                    restrict: {
                        restriction: container,
                        endOnly: false,
                        elementRect: {
                            top: 0,
                            left: 0,
                            bottom: 1,
                            right: 1
                        }
                    },

                    onstart: function(event) {
                        event.target.classList.add('dragging');
                        // 更新容器尺寸(防止窗口大小改变导致限制不准确)
                        self.containerRect = container.getBoundingClientRect();
                    },

                    onmove: function(event) {
                        const target = event.target;
                        const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
                        const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;

                        target.style.transform = `translate(${x}px, ${y}px)`;
                        target.setAttribute('data-x', x);
                        target.setAttribute('data-y', y);
                    },

                    onend: function(event) {
                        const target = event.target;
                        target.classList.remove('dragging');

                        const dragIndex = parseInt(target.id.split('-')[1]);

                        // 重置位置
                        target.style.transform = '';
                        target.removeAttribute('data-x');
                        target.removeAttribute('data-y');

                        // 获取鼠标释放位置(相对于容器)
                        const dropX = event.clientX - self.containerRect.left;
                        const dropY = event.clientY - self.containerRect.top;

                        // 找出释放位置下方的方块
                        const dropBlock = document.elementFromPoint(event.clientX, event.clientY);

                        if (dropBlock && dropBlock.classList.contains('block') &&
                            dropBlock !== target &&
                            !dropBlock.classList.contains('filled')) {

                            const dropIndex = parseInt(dropBlock.id.split('-')[1]);

                            // 交换内容
                            const temp = self.blocks[dragIndex].content;
                            self.$set(self.blocks, dragIndex, {
                                ...self.blocks[dragIndex],
                                content: ''
                            });
                            self.$set(self.blocks, dropIndex, {
                                ...self.blocks[dropIndex],
                                content: temp
                            });
                        }
                    }
                });
            }
        }
    });

完整代码

html 复制代码
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>拖拽交换</title>
        <style>
            .container {
                background-color: #d4d4d4;
                /* 修改背景色 */
                border: 2px solid black;
                height: 320px;
                width: 240px;
                position: relative;
                overflow: hidden;
                /* 防止内容溢出 */
            }

            .block {
                position: absolute;
                width: 50px;
                height: 32px;
                border: 2px solid black;
                border-radius: 5px;
                text-align: center;
                font-weight: bolder;
                cursor: move;
                user-select: none;
                box-sizing: border-box;
                /* 确保边框不增加元素尺寸 */
            }

            .filled {
                background-color: #00ff00;
            }

            .empty {
                background-color: #ffffff;
            }

            .dragging {
                opacity: 0.8;
                z-index: 100;
                box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
            }

            .title {
                font-size: 20px;
                color: red;
                margin-bottom: 10px;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <div class="title">拖拽交换1</div>
            <div class="container" id="dropzone">
                <div v-for="(block, index) in blocks" :key="index" :id="'block-'+index"
                     :class="['block', block.content ? 'filled' : 'empty']"
                     :style="{ top: block.top + 'px', left: block.left + 'px' }">
                    {{ block.content }}
                </div>
            </div>
        </div>

        <script src="js/interact.js"></script>
        <script src="js/vue.min.js"></script>

        <script>
            new Vue({
                el: '#app',
                data: {
                    blocks: [],
                    blockSize: {
                        width: 45,
                        height: 25
                    },
                    margin: {
                        x: 5,
                        y: 10
                    },
                    cols: 4,
                    containerRect: null
                },
                created() {},
                mounted() {
                    this.initBlocks();
                    this.$nextTick(() => {
                        this.containerRect = document.getElementById('dropzone').getBoundingClientRect();
                        this.setupDragAndDrop();
                    });
                },
                methods: {
                    initBlocks() {
                        const blocks = [];
                        const blockWidth = this.blockSize.width + this.margin.x * 2;
                        const blockHeight = this.blockSize.height + this.margin.y * 2;

                        for (let i = 0; i < 24; i++) {
                            const row = Math.floor(i / this.cols);
                            const col = i % this.cols;

                            let cont = '';
                            if (i === 0) cont = '3';
                            if (i == 13) cont = '127';

                            blocks.push({
                                id: i,
                                content: cont,
                                top: row * blockHeight + this.margin.y,
                                left: col * blockWidth + this.margin.x,
                                originalTop: row * blockHeight + this.margin.y,
                                originalLeft: col * blockWidth + this.margin.x
                            });
                        }
                        this.blocks = blocks;
                    },

                    setupDragAndDrop() {
                        const self = this;
                        const container = document.getElementById('dropzone');

                        interact('.block.filled').draggable({
                            inertia: false,
                            autoScroll: false,
                            restrict: {
                                restriction: container,
                                endOnly: false,
                                elementRect: {
                                    top: 0,
                                    left: 0,
                                    bottom: 1,
                                    right: 1
                                }
                            },

                            onstart: function(event) {
                                event.target.classList.add('dragging');
                                // 更新容器尺寸(防止窗口大小改变导致限制不准确)
                                self.containerRect = container.getBoundingClientRect();
                            },

                            onmove: function(event) {
                                const target = event.target;
                                const x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
                                const y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;

                                target.style.transform = `translate(${x}px, ${y}px)`;
                                target.setAttribute('data-x', x);
                                target.setAttribute('data-y', y);
                            },

                            onend: function(event) {
                                const target = event.target;
                                target.classList.remove('dragging');

                                const dragIndex = parseInt(target.id.split('-')[1]);

                                // 重置位置
                                target.style.transform = '';
                                target.removeAttribute('data-x');
                                target.removeAttribute('data-y');

                                // 获取鼠标释放位置(相对于容器)
                                const dropX = event.clientX - self.containerRect.left;
                                const dropY = event.clientY - self.containerRect.top;

                                // 找出释放位置下方的方块
                                const dropBlock = document.elementFromPoint(event.clientX, event.clientY);

                                if (dropBlock && dropBlock.classList.contains('block') &&
                                    dropBlock !== target &&
                                    !dropBlock.classList.contains('filled')) {

                                    const dropIndex = parseInt(dropBlock.id.split('-')[1]);

                                    // 交换内容
                                    const temp = self.blocks[dragIndex].content;
                                    self.$set(self.blocks, dragIndex, {
                                        ...self.blocks[dragIndex],
                                        content: ''
                                    });
                                    self.$set(self.blocks, dropIndex, {
                                        ...self.blocks[dropIndex],
                                        content: temp
                                    });
                                }
                            }
                        });
                    }
                }
            });
        </script>
    </body>
</html>
相关推荐
難釋懷2 分钟前
vue 项目中常用的 2 个 Ajax 库
前端·vue.js·ajax
Qian Xiaoo4 分钟前
Ajax入门
前端·ajax·okhttp
爱生活的苏苏27 分钟前
vue生成二维码图片+文字说明
前端·vue.js
拉不动的猪29 分钟前
安卓和ios小程序开发中的兼容性问题举例
前端·javascript·面试
炫彩@之星35 分钟前
Chrome书签的导出与导入:步骤图
前端·chrome
贩卖纯净水.1 小时前
浏览器兼容-polyfill-本地服务-优化
开发语言·前端·javascript
前端百草阁1 小时前
从npm库 Vue 组件到独立SDK:打包与 CDN 引入的最佳实践
前端·vue.js·npm
夏日米米茶1 小时前
Windows系统下npm报错node-gyp configure got “gyp ERR“解决方法
前端·windows·npm
且白1 小时前
vsCode使用本地低版本node启动配置文件
前端·vue.js·vscode·编辑器
程序研1 小时前
一、ES6-let声明变量【解刨分析最详细】
前端·javascript·es6