HTML块拖拽交换

拖拽交换

效果图如下

请添加图片描述

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页面

<!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

<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
                            });
                        }
                    }
                });
            }
        }
    });

完整代码

<!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>

参考文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值