zoukankan      html  css  js  c++  java
  • react-dnd 拖拽排序

    开始

    最近刚刚做完自定义表单的组件,分享一下拖拽排序。

    效果图

    react-dnd使用说明

    必须是这样格式的,不然会报 找不到上下文

    <DndProvider backend={HTML5Backend}>
       <useDrop>
          <useDrag></useDrag>
       </useDrop>
    </DndProvider>
    

    准备工作

    官方文档

    https://react-dnd.github.io/react-dnd/about

    安装react-dnd、react-dnd-html5-backend

    npm install react-dnd react-dnd-html5-backend
    

    开始开发

    项目是基于ts的,不需要可自行删除

    定义父组件

    const Drag:React.FC<{}> = props => {
      return (
        <div className="drag-wrapper">
          {/* DndProvider组件提供了react-dnd的功能,必须通过backend绑定HTML5Backend*/}
          <DndProvider backend={HTML5Backend}>
            <DragSortComponent/>
          </DndProvider>
        </div>
      )
    }
    
    export default Drag
    

    定义内容组件

    const DragSortComponent:React.FC<{}> = props => {
      const [list,setList] = useState<{id:number,name:string}[]>(List)
      const [itemClass, setItemClass] = useState<{ key: number | null; value: string }>({
        key: null,
        value: '',
      })
      // 拖拽后的值
      const sortItems = useRef<{ dragRow: any; placeRow: any; posi: string }>({
        dragRow: {},
        placeRow: {},
        posi: '',
      })
      // 拖拽结束后的方法
      const onDrop = (item: any, monitor: DropTargetMonitor) => {
        const { dragRow, placeRow, posi } = sortItems.current
        let _map: any[] = JSON.parse(JSON.stringify(list))
        let index1 = _map.findIndex(v => v.id === dragRow.id) // 拖拽的itemIndex
        _map.splice(index1, 1) // 先删掉拖拽的,在获取放置的
        let index = _map.findIndex(v => v.id === placeRow.id) // 放置的itemIndex
        if (index !== -1 && index1 !== -1) {
          _map.splice(posi === 'bottom' ? index + 1 : index, 0, dragRow)
          setList(() => _map)
        }
      }
      // DragSortItemComponent组件是通用排序组件,所以需要在父组件在定义一个useDrop,来改变数据 必须挂载在父级div
      const [, drop] = useDrop({
        accept: 'sort', // 必须和拖拽的accept一致
        drop: onDrop,
        collect: monitor => ({
          isOver: monitor.isOver({ shallow: true }),
          canDrop: monitor.canDrop(),
        }),
      })
      const onItemDragClass = (key:number,value:string) => {
        if (itemClass.value !== value) {
          setItemClass(() => {
            let data = { key, value }
            return data
          })
        }
      }
      const onSortItemChange = (dragRow: any, placeRow: any, posi: string) => {
        sortItems.current = { dragRow, placeRow, posi }
      }
      return (
        <div className="drag-sort-component-wrapper" ref={drop}>
          {
            list.map(v => (
              <DragSortItemComponent
                key={v.id}
                row={v}
                onItemDragClass={onItemDragClass}
                onSortItemChange={onSortItemChange}
                keyName="id"
              >
                <div className={['drag-item ',itemClass.key === v.id ? itemClass.value : ''].join(' ')}>{v.name}</div>
              </DragSortItemComponent>
            ))
          }
        </div>
      )
    }
    

    定义拖拽排序组件

    /**
     * 通用拖拽排序的容器
     * @param row 当前行
     * @param onItemDragClass 拖拽过程的样式 top | bottom
     * @param onSortItemChange 拖拽结束后返回的值 dragRow 当前拖拽 placeRow 放置的,posi 位置 top | bottom
     * @param keyName 键名
     */
    type IProps = {
      row: any,
      onItemDragClass: (key:number,value:string) => void,
      onSortItemChange: (dragRow: any, placeRow: any, posi: string) => void // 排序后
      keyName: string
    }
    const DragSortItemComponent:React.FC<IProps> = props => {
      const { row,onItemDragClass,onSortItemChange,keyName } = props
      const ref = useRef<HTMLDivElement>(null)
      /**
       * 拖拽容器
       */
      const [, drop] = useDrop({
        // 定义拖拽的类型
        accept: 'sort',
        drop: (item, monitor) => {
          const didDrop = monitor.didDrop()
          if (didDrop) {
            return
          }
        },
        canDrop: (item, mointor) => {
          // 阻止默认拖拽释放
          onItemDragClass(row[keyName], '')
          return false
        },
        hover: (item: any, monitor) => {
          const didHover = monitor.isOver({ shallow: true })
          if (didHover) {
            // 拖拽目标的id
            const dragIndex = item[keyName]
            // 放置目标id  可以用index | id 只要是number,数据里唯一的就可以
            const hoverIndex = row[keyName]
            // 如果一样不处理
            if (dragIndex === hoverIndex) {
              onItemDragClass(row[keyName], '')
              return
            }
            // 获取放置的位置
            const hoverBoundingRect = ref.current?.getBoundingClientRect() as DOMRect
            // 获取放置的Y轴中点
            const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2
            // 获取拖拽目标偏移量
            const clientOffset = monitor.getClientOffset() as XYCoord
            const hoverClientY = clientOffset.y - hoverBoundingRect.top
            if (dragIndex !== hoverIndex) {
              if (hoverMiddleY < hoverClientY) {
                onItemDragClass(row[keyName], 'bottom')
              } else {
                onItemDragClass(row[keyName], 'top')
              }
              // 如果不做成通用拖拽容器,把参数存起来,把这个放在useDrag的end方法里,
              onSortItemChange(item, row, hoverMiddleY < hoverClientY ? 'bottom' : 'top')
            }
          }
        },
        collect: monitor => ({
          isOver: monitor.isOver({ shallow: true }),
          canDrop: monitor.canDrop(),
        }),
      })
      /**
       * 定义拖拽
       * isDragging 是否拖拽
       */
      const [{ isDragging }, drag] = useDrag({
        item: { ...row, type: 'sort'},
        end: () => {
          // onSortItemChange(item, row, hoverMiddleY < hoverClientY ? 'bottom' : 'top')
        },
        collect: monitor => ({
          isDragging: monitor.isDragging(),
          didDrop: monitor.isDragging(),
        }),
      })
      drop(drag(ref))
      return (
        <div ref={ref} style={{ opacity: isDragging ? 0 : 1 }}>
          {props.children}
        </div>
      )
    }
    

    github地址

    https://github.com/GeFei-someone/drag-sort

  • 相关阅读:
    JavaScript验证长度
    createTextRange用法
    keyPress keyUp keyDown 联系与区别
    获取text、select、radio、checkbox的值
    深入浅出JavaScript (五) 详解Document.write()方法
    深入浅出JQuery (三) 图片预览效果
    BS部分整体学习
    深入浅出JQuery (二) 选择器
    深入浅出jQuery (五) 如何自定义UIDialog?
    深入浅出 JQuery (一) 浅析JQuery
  • 原文地址:https://www.cnblogs.com/feiyu159/p/14267401.html
Copyright © 2011-2022 走看看