列表拖动排序的解决方案有多种,个人感觉最简单的就是zent 提供的 Sortable 组件了
一、zent - Sortable 拖拽排序(推荐、简单明了)
zent 官网 https://zent-contrib.gitee.io/zent/zh/component/sortable
zent githubApi文档 https://zent-contrib.gitee.io/zent/apidoc/
用法:
yarn add zent
# or
npm install zent --save
import { Sortable, Card } from 'zent';
class Simple extends React.Component {
state = {
list: [ {name:'Arvin'},{name:'Jack'},{name: 'Bob'},{name: 'Nick'}]
}
// onChane 获取的是排序后的数组
handleChange = (items) => {
this.setState({ list: items });
}
render() {
const { list } = this.state;
return (
<Sortable
items={list}
onChange={this.handleChange}
// filterClass="stop-to-drag" 不允许拖动的item 类名
>
{
// list.map(({ name } ) => 这里是第一项不允许拖动
// <Card key={name} className={index?"":"stop-to-drag"}>{name}</Card>)
list.map(({ name } ) => <Card key={name}>{name}</Card>)
}
</Sortable>
);
}
}
just so so!!!
二、react-dnd (学习成本很大,不推荐)
官网文档:https://react-dnd.github.io/react-dnd/about
三、纯js手撸
代码地址:https://codepen.io/jhonyoung/pen/PeGpNL?editors=0110
.drag-up {
animation: dragup ease 0.2s 1;
animation-fill-mode: forwards;
border: solid 2px #cccccc;
}
.drag-down {
animation: dragdown ease 0.2s 1;
animation-fill-mode: forwards;
border: solid 2px #cccccc;
}
@keyframes dragup
{
from { margin-top:10px; }
to { margin-top:60px; }
}
@keyframes dragdown
{
from { margin-bottom:10px; margin-top:60px; }
to { margin-bottom:60px; margin-top:10px; }
}
ul {
list-style: none
}
class List extends React.Component {
constructor(props) {
super(props);
this.state = {...props};
}
dragStart(e) {
this.dragged = e.currentTarget;
}
dragEnd(e) {
this.dragged.style.display = 'block';
e.target.classList.remove("drag-up");
this.over.classList.remove("drag-up");
e.target.classList.remove("drag-down");
this.over.classList.remove("drag-down");
var data = this.state.data;
var from = Number(this.dragged.dataset.id);
var to = Number(this.over.dataset.id);
data.splice(to, 0, data.splice(from, 1)[0]);
//set newIndex to judge direction of drag and drop
data = data.map((doc, index)=> {
doc.newIndex = index + 1;
return doc;
})
this.setState({data: data});
}
dragOver(e) {
e.preventDefault();
this.dragged.style.display = "none";
if (e.target.tagName !== "LI") {
return;
}
//判断当前拖拽target 和 经过的target 的 newIndex
const dgIndex = JSON.parse(this.dragged.dataset.item).newIndex;
const taIndex = JSON.parse(e.target.dataset.item).newIndex;
const animateName = dgIndex > taIndex ? "drag-up" : "drag-down";
if (this.over && e.target.dataset.item !== this.over.dataset.item) {
this.over.classList.remove("drag-up", "drag-down");
}
if(!e.target.classList.contains(animateName)) {
e.target.classList.add(animateName);
this.over = e.target;
}
}
render() {
var listItems = this.state.data.map((item, i) => {
return (
<li
data-id={i}
key={i}
style={{height: "60px", border: "solid 1px #cccccc",
margin: "10px 30%", borderRadius: "5px", backgroundColor: "green",
color: "#ffffff"}}
draggable='true'
onDragEnd={this.dragEnd.bind(this)}
onDragStart={this.dragStart.bind(this)}
data-item={JSON.stringify(item)}
>{item.color}</li>
)
});
return (
<ul onDragOver={this.dragOver.bind(this)} className ="contain">
{listItems}
</ul>
)
}
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [
{ newIndex: 1, color: "red" },
{ newIndex: 2, color: "green" },
{ newIndex: 3, color: "blue" },
{ newIndex: 4, color: "yellow" },
{ newIndex: 5, color: "orange" },
{ newIndex: 6, color: "black" }
]
}
}
render() {
return <List data={this.state.data} />
}
}
ReactDOM.render(
<App />,
document.getElementById('app'),
);
四、案例三 react Hooks 简写版本
App.js
<Drag
data={data} // 必填
onChange={(newArr:any)=>{
console.log(`newArr`, newArr)
}}
>
{data.map((item,index)=>{
return <div
key={index} // 使用的key值用来筛选
className="drag-item"
>
{item.color}
</div>
})}
</Drag>
Drag.js
import React, { useState } from 'react'
interface Dragtype {
children?:React.ReactElement[],
onChange:any,
data: {
newIndex: number;
color: string;
}[]
}
const Drag: React.FC<Dragtype> = (props) =>{
const [list,setList] = useState<any|React.ReactElement>(props.children)
const [current,setCurrent] = useState<any>(null)
const [endIndex,setEndIndex] = useState<any>(null)
const onDragStart = (index:number) => {
setCurrent(index)
}
const onDragEnd = (index:number) =>{
if(endIndex!==index){
let _list:any = [...list];
_list[endIndex] = list[current];
_list[current] = list[endIndex];
setList(_list)
if(props.onChange){
// 使用的key值用来筛选
const newArr = _list.map((i:any)=> props.data[i.key])
props.onChange(newArr)
}
}
setEndIndex(null)
setCurrent(null)
}
/**
* @description: 可以获取到当前滑动到第几个
* @param {any} e 下表数目
* @param {number} index
* @return {*}
*/
const onDragOver = (index:number) => {
setEndIndex(index)
}
return <div style={{ border:"1px solid red" }}>
{list.map((item:any,index:any) => <div
key={item.key}
draggable='true'
onDragStart={()=>onDragStart(index)}
onDragOver={()=>onDragOver(index)} // 获取到最终的位置
onDragEnd={()=>onDragEnd(index)}
>{item}</div>)}
</div>;
}
export default Drag;