const list = [
{
title: '波妞1',
src: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.alicdn.com%2Fi2%2F1628650609%2FO1CN01VZegdd1GMxn2LDJjm_%21%211628650609.jpg&refer=http%3A%2F%2Fimg.alicdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1629511811&t=a164044af84bc6b7faa2cc109fc6b808'
},
{
title: '魔女2',
src: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg15.3lian.com%2F2015%2Fh1%2F130%2Fd%2F41.jpg&refer=http%3A%2F%2Fimg15.3lian.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1629511721&t=908cba2a5319c3d2d55a9cc0955de25c'
},
{
title: '龙猫3',
src: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fgss0.baidu.com%2F-Po3dSag_xI4khGko9WTAnF6hhy%2Fzhidao%2Fpic%2Fitem%2F4034970a304e251fae75ad03a786c9177e3e534e.jpg&refer=http%3A%2F%2Fgss0.baidu.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1629511755&t=27314f2d34ebb983a1c1fa6393c781e4'
}
]
function App() {
return (
<div className="App">
drag list by hook
<DraggableList list={list} />
</div>
);
}
export default App;
function DraggableList({ list }) {
const { dragList, createDraggerPorps, createDropperProps } = useDraggable(list);
return dragList.map((item, i) => {
if (item.type === "BAR") {
return <Bar id={i} key={item.id} {...createDropperProps(i)} />
} else {
return (
<Draggable {...createDraggerPorps(i)}>
<Card {...item.data} />
</Draggable>
)
}
})
}
function Draggable({ children, eventHandlers, dragging, id }) {
return <div
className={cls("draggable", [[dragging === id, "dragging"]])}
// 可拖拽
draggable={true}
{...eventHandlers}
>
{children}
</div>
}
function Bar({ id, dragging, dragOver, eventHandlers }) {
if (id === dragging + 1) {
return null;
}
return <div
className={cls("draggable-bar", [[dragOver === id, "dragOver"]])}
{...eventHandlers}>
<div
className="inner"
style={{
height: id === dragOver ? '80px' : '0px',
}}
/>
</div>
}
function Card({ src, title }) {
return <div className="card">
<img src={src} />
<span>{title}</span>
</div>
}
function cls(def, conditions) {
const list = [def];
conditions.forEach(cond => {
if (cond[0]) {
list.push(cond[1])
}
})
return list.join(" ")
}
- 拖拽相关的逻辑全部维护在自定义hook中
- 涉及到的拖拽事件:
- onDragStart
- onDragEnd
- onDragOver
- onDragLeave
- onDrop
const DRAGGABLE = 'DRAGGABLE';
const BAR = 'BAR';
function draggable(item, id) {
return {
type: DRAGGABLE,
id,
data: item
}
}
function insertBars(list) {
let i = 0;
const newBar = () => {
return {
type: BAR,
id: i++
}
}
const arr = [newBar()].concat(
...list.map(item => {
return [draggable(item, i++), newBar()]
})
)
return arr;
}
function clacChanging(list, drag, drop) {
list = list.slice();
const dragItem = list[drag];
const dir = drag > drop ? -2 : 2;
const end = dir > 0 ? drop - 1 : drop + 1;
for (let i = drag; i != end; i += dir) {
list[i] = list[i + dir]
}
list[end] = dragItem;
return list;
}
function useDraggable(list) {
const [dragList, setDragList] = useState(insertBars(list));
const [dragOver, setDragOver] = useState(null);
const [dragging, setDragging] = useState(null);
return {
dragList,
createDropperProps: id => {
return {
dragging,
dragOver,
eventHandlers: {
onDragOver: e => {
e.preventDefault();
setDragOver(id);
},
onDragLeave: e => {
e.preventDefault();
setDragOver(null);
},
onDrop: e => {
console.log('zhixing ')
e.preventDefault();
setDragOver(null);
setDragList(list => {
return clacChanging(list, dragging, id)
})
}
}
}
},
createDraggerPorps: id => {
return {
id,
key: id,
dragging,
eventHandlers: {
onDragStart: () => {
setDragging(id);
},
onDragEnd: () => {
setDragging(null);
}
}
}
}
};
}
.card {
display: flex;
align-items: center;
padding: 10px;
box-shadow: grey 1px 2px 3px;
cursor: pointer;
user-select: none;
}
.card img {
border-radius: 36px;
width: 72px;
height: 72px;
}
.card span {
margin-left: 20px;
}
.draggable-bar {
padding: 10px 0;
transition: background-color 1s ease-out;
}
.inner{
transition: height 0.3s ease;
margin: 10px 0;
background-color: rgba(0,0,0,.1);
}
.draggable {
background: white;
transition: all 0.3s ease;
}
.dragging {
opacity: 0;
position: fixed;
width: 100%;
}