要用到的 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組件;也可以使用函數的方式,通過調用函數來實現拖拽功能;還可以使用指令的方式,通過指定元素的選擇器來實現拖拽功能??傆幸豢钸m合您!
<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 | 拖拽方向,默認自動判斷??蛇x值有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有了初步的了解。作為前端工程師,掌握這樣一款功能強大、靈活易用的前端組件,將會為您的項目開發帶來很大的便利??靵韲L試一下吧,讓拖拽功能變得更加簡單愉快!
以上就是本次的技術分享,希望對大家有所幫助,謝謝大家的閱讀!
作者:墩墩大魔王丶
鏈接:https://juejin.cn/post/7342718848817561612
*請認真填寫需求信息,我們會在24小時內與您取得聯系。