要用到的 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小时内与您取得联系。