要用到的 JS 事件有:
onmousedown:鼠标点下事件
onmousemove:鼠标移动事件
onmouseup:鼠标放开事件
具体代码如下:
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>列拖动</title> <style type="text/css"> body,ul,li {padding: 0; margin: 0;} ul, li {list-style: none;} .left {width: 300px; float: left;margin: 3px;} .right {width: 300px; float: left; margin:3px;} .column {width: 200px;height: 30px; line-height: 30px; background-color: #AACCFF; margin: 8px 5px; text-align: center; cursor: pointer;} .columnOld {width: 200px; height: 30px; line-height: 30px; background-color: #CCC; margin: 8px 5px; text-align: center;} .target {border: 1px solid #CCC; background-color: #FFF5EE;box-shadow: 0 0 8px #CCC; -moz-box-shadow: 0 0 8px #CCC; -webkit-box-shadow: 0 0 8px #CCC;} .container {width: 600px; height: 200px; display: block;} </style> <script type="text/javascript" src="jquery.js"></script> <script type="text/javascript"> window.onload = function(){ var Lis = document.getElementsByClassName("column"); var container = document.getElementsByClassName("container")[0]; for (var i = Lis.length - 1; i >= 0; i--) { var obj = Lis[i]; var source = obj.parentNode; var target = document.getElementById("container"); moveColumn(obj, target, source); } } /** * 字段拖拽事件 */ function moveColumn(obj, target, source) { // 1. 获取事件 obj.onmousedown = function(event) { var ev = event || window.event; // 2. 复制新节点,设置透明度和innerHTML,class var newObj = document.createElement("li"); newObj.className = 'column'; newObj.style.opacity = '0.5'; newObj.style.filter = 'alpha(opacity:50)'; newObj.innerHTML = obj.innerHTML; newObj.style.margin = '0'; newObj.style.position = 'absolute'; newObj.style.zIndex = '5'; newObj.source = source; // 3. 计算点击点在 obj 上的位置 var disH = ev.clientY - obj.offsetTop; var disL = ev.clientX - obj.offsetLeft; // 4. 设置绝对定位的top和left newObj.style.top = obj.offsetTop + 'px'; newObj.style.left = obj.offsetLeft + 'px'; // 5. 添加节点 obj.parentNode.appendChild(newObj); // 6. 鼠标移动事件 document.onmousemove = function(e) { // 1. 获取事件 var e = e || window.event; // 2. 获取鼠标位置,设置newObj的定位 var L = e.clientX - disL; var T = e.clientY - disH; newObj.style.top = T + 'px'; newObj.style.left = L + 'px'; // 3. source 背景色和边框变换 $(target).addClass("target"); obj.className = "columnOld"; } // 7. 鼠标松开事件 document.onmouseup = function() { $(target).removeClass("target"); // target.class = ''; var not = newObj.offsetTop; var nol = newObj.offsetLeft; var tot = target.offsetTop; var tol = target.offsetLeft; if (not >= tot && nol >= tol && not <= tot + target.offsetHeight && nol <= tol + target.offsetWidth) { newObj.removeAttribute('style'); newObj.className = "column"; newObj.style.float = "left"; target.appendChild(newObj); backColumn(newObj, source, target); var id = obj.id; newObj.id = id; obj.id = id + "_old"; obj.onmousedown = null; } else { obj.className = "column"; obj.parentNode.removeChild(newObj); } document.onmouseup = null; document.onmousemove = null; } } } function backColumn(obj, target, source) { var Lis = source.getElementsByTagName('li'); var lineNum = Math.floor(source.offsetWidth / 210); var liFirst = Lis[0].offsetTop; var liHeight = Lis[0].offsetHeight; var liWidth = Lis[0].offsetWidth; // 1. 获取事件 obj.onmousedown = function(event) { var ev = event || window.event; // 2. 复制新节点,设置透明度和innerHTML,class var newObj = document.createElement("li"); newObj.className = 'column'; newObj.style.opacity = '0.5'; newObj.style.filter = 'alpha(opacity:50)'; newObj.innerHTML = obj.innerHTML; newObj.style.margin = '0'; newObj.style.position = 'absolute'; newObj.style.zIndex = '5'; // 3. 计算点击点在 obj 上的位置 var disH = ev.clientY - obj.offsetTop; var disL = ev.clientX - obj.offsetLeft; // 4. 设置绝对定位的top和left newObj.style.top = obj.offsetTop + 'px'; newObj.style.left = obj.offsetLeft + 'px'; // 5. 添加节点 obj.parentNode.appendChild(newObj); // 6. 添加空白节点(占位) var blank = document.createElement("li"); blank.className = 'column'; blank.style.backgroundColor = '#63B8FF'; blank.style.float = "left"; // 6. 鼠标移动事件 document.onmousemove = function(e) { // 1. 获取事件 var e = e || window.event; // 2. 获取鼠标位置,设置newObj的定位 var L = e.clientX - disL; var T = e.clientY - disH; newObj.style.top = T + 'px'; newObj.style.left = L + 'px'; // 3. source 背景色和边框变换 $(target).addClass("target"); obj.className = "columnOld"; // 根据当前拖拽到的位置计算其重新排序后的位置 var line = lineNum * Math.round((T - liFirst)/liHeight) var n = line + Math.floor(L / liWidth); // 将空白节点插入到该位置 // if () source.insertBefore(blank,source.children[n]); } // 7. 鼠标松开事件 document.onmouseup = function() { $(target).removeClass("target"); // target.class = ''; var not = newObj.offsetTop; var nol = newObj.offsetLeft; var tot = target.offsetTop; var tol = target.offsetLeft; if (not >= tot && nol >= tol && not <= tot + target.offsetHeight && nol <= tol + target.offsetWidth) { var oldObj = document.getElementById(obj.id + '_old'); oldObj.className = 'column'; source.removeChild(newObj); source.removeChild(obj); oldObj.id = obj.id; moveColumn(oldObj, source, target); obj.onmousedown = null; } else { obj.className = "column"; obj.parentNode.removeChild(newObj); // 将被拖拽的元素插入到空白节点的位置 if (blank.parentNode == source) { source.insertBefore(obj,blank); } // 删除拖拽样式 // obj.removeAttribute('style'); } if (blank.parentNode == source) { // 删除空白节点 source.removeChild(blank); } document.onmouseup = null; document.onmousemove = null; } } } function getStyle(obj, attr) { if (obj.currentStyle) { return obj.currentStyle[attr]; } else { return getComputedStyle(obj, false)[attr]; } } </script> </head> <body> <div style="overflow: hidden;"> <ul class="left"> <li class="column" id="c1">字段a-1</li> <li class="column" id="c2">字段a-2</li> <li class="column" id="c3">字段a-3</li> <li class="column" id="c4">字段a-4</li> <li class="column" id="c5">字段a-5</li> </ul> <ul class="right"> <li class="column" id="c6">字段b-1</li> <li class="column" id="c7">字段b-2</li> <li class="column" id="c8">字段b-3</li> <li class="column" id="c9">字段b-4</li> <li class="column" id="c10">字段b-5</li> </ul> </div> <div class="container" id="container"> </div> </body> </html>
最终效果
比较简单,欢迎留言指正,代码比较冗余,还没来得及做整理,往见谅。
看效果图:
拖拽前
拖拽后
我在header处加了的id 然后通过其点击事件给其父容器根据鼠标移动来判断位置,写的比较简陋,只能按住黑框部分处才能进行拖拽
附上代码:
大家好,我是墩墩大魔王丶,今天我将为大家介绍一款功能强大、灵活易用的前端组件——VueDraggablePlus。作为前端工程师,我们经常会遇到需要实现拖拽功能的场景,而VueDraggablePlus正是为了解决这一痛点而诞生的。让我们一起来看看它的特点和用法吧!
VueDraggablePlus是一个功能齐全、无缝迁移、灵活使用的拖拽组件,完全继承了Sortable.js的所有功能,支持Vue 3和Vue 2,使用TypeScript编写并提供完整的TS文档,支持双向绑定和自定义容器,可以通过组件、指令或函数式调用实现拖拽功能。
官网: alfred-skyblue.gitee.io/vue-draggab…
在日常开发中,我们经常会遇到需要实现拖拽列表的需求,但是传统的方法往往需要将拖拽功能作为列表的直接子元素来实现,这在一些情况下会受到限制。比如,当我们使用一些组件库时,如果组件库中没有提供列表根元素的插槽,我们就很难实现拖拽列表。这时,VueDraggablePlus就派上了用场!它可以让你在任何元素上使用拖拽列表,通过指定元素的选择器,来获取到列表根元素,然后将列表根元素作为拖拽容器,轻松实现拖拽功能。
npm install vue-draggable-plus
<template>
<VueDraggable ref="el" v-model="list">
<div v-for="item in list" :key="item.id">
{{ item.name }}
</div>
</VueDraggable>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { VueDraggable } from 'vue-draggable-plus'
const list = ref([
{
name: 'Joao',
id: 1
},
{
name: 'Jean',
id: 2
},
{
name: 'Johanna',
id: 3
},
{
name: 'Juan',
id: 4
}
])
</script>
alfred-skyblue.gitee.io/vue-draggab…
VueDraggablePlus提供了多种使用方式,您可以根据自己的需求选择最适合的方式来实现拖拽功能。您可以使用组件的方式,在需要拖拽的元素上直接添加VueDraggablePlus组件;也可以使用函数的方式,通过调用函数来实现拖拽功能;还可以使用指令的方式,通过指定元素的选择器来实现拖拽功能。总有一款适合您!
<template>
<div
v-draggable="[
list,
{
animation: 150,
}
]"
>
<div
v-for="item in list"
:key="item.id"
>
{{ item.name }}
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { vDraggable } from 'vue-draggable-plus'
const list = ref([
{
name: 'Joao',
id: 1
},
{
name: 'Jean',
id: 2
},
{
name: 'Johanna',
id: 3
},
{
name: 'Juan',
id: 4
}
])
function onStart() {
console.log('start')
}
function onUpdate() {
console.log('update')
}
</script>
这里我们使用了VueDraggablePlus组件的方式来实现拖拽列表,将列表项包裹在VueDraggablePlus组件内,即可轻松实现拖拽功能。
VueDraggablePlus完全继承了Sortable.js的所有功能,让您可以尽情享受拖拽的乐趣。无论是拖拽排序、拖拽上传还是拖拽调整布局,VueDraggablePlus都可以轻松应对,让您的前端开发更加便捷高效。
VueDraggablePlus不仅功能强大,而且还支持多种场景的应用。比如双列表拖拽、克隆、自定义克隆、指定元素触发、指定目标容器、内置动画合并等,为您的项目提供了更多的可能性。
参数 | 说明 | 类型 | 默认值 |
animation | 拖动时显示动画 | Number | 0 |
chosenClass | 被选中项的 CSS 类名 | String | 'sortable-chosen' |
delay | 选中拖拽延时 | Number | 0 |
delayOnTouchOnly | touch 事件延迟 | Number | 0 |
direction | 拖拽方向,默认自动判断。可选值有horizontal 和 vertical | String | - |
disabled | 是否禁止拖拽 | Boolean | false |
dragClass | 拖拽项类名 | String | 'sortable-drag' |
draggable | 指定元素内的哪些项目应该是可拖动的 | String | - |
emptyInsertThreshold | 拖动时鼠标必须与空可排序项的距离(以像素为单位),以便将拖动元素插入到该可排序项中, 设置为0禁用此功能。 | Number | 5 |
easing | 简化动画。 | Easing | - |
fallbackClass | 当使用forceFallback的时候,被复制的dom的css类名 | String | sortable-fallback |
fallbackOnBody | 将cloned DOM 元素挂到body元素上。 | Boolean | false |
fallbackTolerance | 以像素为单位指定鼠标在被视为拖动之前应该移动多远。 | Number | 0 |
filter | 不需要进行拖动的元素 | String | - |
forceFallback | 忽略 HTML5拖拽行为,强制回退 | Boolean | false |
ghostClass | drop placeholder的css类名 | String | 'sortable-ghost' |
group | 要将元素从一个列表拖到另一个列表中,两个列表必须具有相同的group 值。您还可以定义列表是否可以被移出、或者克隆以及接收其他列表元素。详情查阅上方TS类型定义 | Group | - |
handle | 设置可拖拽句柄的css类名,如果不设置,默认对目标元素的子列表操作进行拖拽 | String | - |
invertSwap | 如果设置为 true,将始终使用反向交换区 | Boolean | false |
invertedSwapThreshold | 反向交换阈值,默认情况下将设置为swapThreshold 值 | Number | - |
preventOnFilter | 触发filter时调用event.preventDefault() | Boolean | true |
removeCloneOnHide | 删除不显示的克隆元素,而不是仅仅隐藏它 | Boolean | true |
sort | 定义列表单元是否可以在列表容器内进行拖拽排序 | Boolean | true |
swapThreshold | 交换区的阈值 | Number | 1 |
touchStartThreshold | 在取消延迟拖动事件之前点应该移动多少像素 | Number | 1 |
setData | 传递一个函数,函数的第一个参数为DataTransfer类型,第二个参数为HTMLElement 类型 | Function | - |
scroll | 是否启用滚动 | Boolean or HTMLElement | true |
scrollFn | 自定义滚动 | ScrollFn | - |
scrollSensitivity | 鼠标必须离边缘多近才能开始滚动,单位 px | Number | - |
scrollSpeed | 滚动速度(ms/px) | Number | - |
bubbleScroll | 将自动滚动应用于所有父元素,以便更轻松地移动 | Boolean | true |
事件函数名称 | 说明 | 类型 |
onChoose | 元素被选中时触发 | ((event: SortableEvent) => void) |
onUnchoose | 元素取消选中时触发 | ((event: SortableEvent) => void) |
onStart | 拖拽开始时触发 | ((event: SortableEvent) => void) |
onEnd | 拖拽结束时触发 | ((event: SortableEvent) => void) |
onAdd | 元素从一个列表拖拽到另一个列表时触发 | ((event: SortableEvent) => void) |
onUpdate | 元素顺序更新时触发 | ((event: SortableEvent) => void) |
onSort | 列表任何更改都会触发 | ((event: SortableEvent) => void) |
onRemove | 元素从列表中移除时触发 | ((event: SortableEvent) => void) |
onFilter | 拖拽一个被过滤元素时触发 | ((event: SortableEvent) => void) |
onMove | 拖拽移动时触发 | ((event: MoveEvent,originalEvent: Event) => void) |
onClone | 克隆一个元素时触发 | ((event: SortableEvent) => void) |
onChange | 拖拽元素改变位置时触发 | `((event: SortableEvent) => |
通过 监听 onStart 事件, 将当前选中项的背景设置为白透明的白色。
其中 SortableEvent 的 item 即当前拖拽项, 通过改变它的 style 即可。
通过 监听 onEnd 事件, 在拖拽结束时 ,将当前选中项的背景颜色还原。
<template>
<VueDraggable ref="el" v-model="list" @start="onStart" @end="onEnd">
<div v-for="item in list" :key="item.id" class="drag-item">
<NodejsButton :title="item.name" />
</div>
</VueDraggable>
</template>
<script setup>
import { ref } from 'vue'
import NodejsButton from '@/components/button/nodejs-button.vue'
import { VueDraggable } from 'vue-draggable-plus'
const list = ref([
{
name: 'Aoooooo',
id: 1
},
{
name: 'Boooooo',
id: 2
},
{
name: 'Coooooo',
id: 3
},
{
name: 'Doooooo',
id: 4
}
])
const onStart = (e) => {
e.item.style.backgroundColor = '#ffffff22'
}
const onEnd = () => {}
</script>
<style>
.drag-item {
width: 240px;
margin-bottom: 15px;
cursor: grab;
&:last-of-type {
margin-bottom: 0;
}
}
</style>
通过本文的介绍,相信大家已经对VueDraggablePlus有了初步的了解。作为前端工程师,掌握这样一款功能强大、灵活易用的前端组件,将会为您的项目开发带来很大的便利。快来尝试一下吧,让拖拽功能变得更加简单愉快!
以上就是本次的技术分享,希望对大家有所帮助,谢谢大家的阅读!
作者:墩墩大魔王丶
链接:https://juejin.cn/post/7342718848817561612
*请认真填写需求信息,我们会在24小时内与您取得联系。