原生draggable实现拖拽列表
一个原生实现的多列表拖拽,和拖拽排序。
类似视频编辑器的时间轴拖拽,资源拖拽。
效果如视频,代码如下。
index.vue
<template>
<p>文件列表</p>
<div class="file-list">
<div class="file" v-for="(file,index) in fileList"
draggable="true"
@dragstart="fileDragStart($event,file)"
@dragend="fileDragEnd($event,file)"
:key="'file'+index">
{
{ file.name }}
</div>
</div>
<div>时间轴{
{ moveIn }}</div>
<div class="line-list" :class="{'line-in':lineIn}"
@dragenter="lineDragEnter"
@dragleave="lineDragLeave"
@dragover="lineDragOver"
@drop="lineDropFile">
<transition-group name="list">
<div class="line" v-for="(line,index) in lineList"
:key="'line'+line.key"
:style="style(line)"
:title="line.name"
@dragstart="lineDragStart($event,index)"
@dragend="lineDragEnd"
draggable="true"
@dragenter="lineItemDragEnter($event,index)"
@dragleave="lineItemDragLeave"
@drop.stop="lineItemDropFile(index)"
>
{
{ line.name }}
</div>
</transition-group>
</div>
<!-- 隐藏对象-->
<div class="hidden">
<div id="move" ref="moveBlock">
{
{ nowFile.name }}
</div>
</div>
</template>
<script setup>
import { randColor } from '@/utils/color.js' // 随机生成颜色
import { uuid } from '@/utils/key.js'// 生成唯一值
import { ref } from 'vue'
const fileList = ref( [
{ name: '视频1.mp4' , type: 'video' , duration: 10 } ,
{ name: '音频2.mp4' , type: 'audio' , duration: 60 } ,
{ name: '视频3.mp4' , type: 'video' , duration: 70 } ,
{ name: '音频4.mp4' , type: 'audio' , duration: 80 } ,
] )
const lineList = ref( [] )
const moveBlock = ref( null )
const nowFile = ref( { name: '' } )
const lineIn = ref( false )
let dragType = 'create'
let moveIndex = ''
let moveIn = ''
const style = ( file ) => {
return {
background: file.color ,
width: file.duration * 5 + 'px'
}
}
/**
* 文件列表拖拽开始
* @param $event
* @param file
*/
const fileDragStart = ( $event , file ) => {
console.log( '文件列表拖拽开始' , $event , file )
dragType = 'create'
nowFile.value = file
let width = nowFile.value.duration * 2
moveBlock.value.style.width = (width > 270 ? 270 : width) + 'px'
$event.dataTransfer.setDragImage( moveBlock.value , 0 , 0 )
}
/**
* 文件列表拖拽结束
* @param $event
* @param file
*/
const fileDragEnd = ( $event , file ) => {
console.log( '文件列表拖拽结束' , $event , file )
lineIn.value = false
}
/**
* 时间轴进入
* @param $event
* @constructor
*/
const lineDragEnter = ( $event ) => {
console.log( '时间列表进入' , $event )
$event.preventDefault(); //阻止默认事件
lineIn.value = true
}
/**
* 时间轴离开
* @param $event
* @param file
* @constructor
*/
const lineDragLeave = ( $event , file ) => {
console.log( '时间列表离开' , $event )
$event.preventDefault(); //阻止默认事件
lineIn.value = false
}
/**
* 时间轴阻止默认
* @param $event
* @constructor
*/
const lineDragOver = ( $event ) => {
$event.preventDefault(); //阻止默认事件
}
/**
* 时间轴放入
* @param $event
*/
const lineDropFile = ( $event ) => {
console.log( '时间列表放入' ,dragType, $event )
// 放在空的地方
if ( dragType === 'create' ) {
let color = randColor()
let file = {
key: uuid() ,
name: nowFile.value.name ,
type: nowFile.value.type ,
duration: nowFile.value.duration ,
color: color
}
console.log( 'file' , file )
lineList.value.push( file )
}
}
/**
* 时间轴拖动开始
* @param $event
* @constructor
*/
const lineDragStart = ( $event , index ) => {
console.log( '时间轴拖动开始' , $event )
dragType = 'move'
moveIndex = index
}
/**
* 时间轴拖动结束
* @param $event
* @constructor
*/
const lineDragEnd = ( $event ) => {
console.log( '时间轴拖动结束' , $event )
moveIndex = ''
}
/**
* 时间轴内容进入
* @param $event
* @constructor
*/
const lineItemDragEnter = ( $event,index ) => {
console.log( '时间轴进入' , $event )
$event.preventDefault(); //阻止默认事件
moveIn = index
}
/**
* 时间轴内容离开
* @param $event
* @constructor
*/
const lineItemDragLeave = ( $event ) => {
console.log( '时间轴离开' , $event )
$event.preventDefault(); //阻止默认事件
moveIn = ''
}
/**
* 时间轴放入
* @param index
*/
const lineItemDropFile = ( index ) => {
console.log( '时间轴放入' , index )
// 放在某个轴上
if ( dragType === 'create' ) {
let color = randColor()
let file = {
key: uuid() ,
name: nowFile.value.name ,
type: nowFile.value.type ,
duration: nowFile.value.duration ,
color: color
}
console.log( 'file' , file )
lineList.value.splice(index,0,file)
}
// 移动
if ( dragType === 'move' ) {
console.log( '移动' ,moveIndex,index)
let list = lineList.value
if(moveIndex > index){
const item = lineList.value[moveIndex]
list.splice(moveIndex,1)
list.splice(index,0,item)
}else{
const item = lineList.value[moveIndex]
console.log('移动',item,list)
list.splice(moveIndex,1)
list.splice(index,0,item)
console.log("移动到后面",list)
}
lineList.value = list
}
}
</script>
<style lang="less" scoped>
.hidden {
position: fixed;
left: 0;
top: -100px;
#move {
min-width: 20px;
height: 20px;
background: red;
border: 1px solid #07b3c9;
overflow: hidden;
}
}
.line-list {
height: 200px;
overflow-y: auto;
.line {
user-select: none;
overflow: hidden;
height: 20px;
white-space: nowrap; /*不显示的地方用省略号...代替*/
text-overflow: ellipsis; /* 支持 IE */
}
}
.line-in {
background: #999;
}
.list-move, /* 对移动中的元素应用的过渡 */
.list-enter-active,
.list-leave-active {
transition: all 0.5s ease;
}
.list-enter-from,
.list-leave-to {
opacity: 0;
transform: translateX(30px);
}
/* 确保将离开的元素从布局流中删除
以便能够正确地计算移动的动画。 */
.list-leave-active {
position: absolute;
}
</style>