美文网首页
react-dnd 使用

react-dnd 使用

作者: skoll | 来源:发表于2020-08-24 14:29 被阅读0次

优势

1 .遇到一个不能忍受的问题,就是拖拽的时候那个禁止按钮无法替换。看下这个拖拽组件有什么东西是别的不能替代的
2 .React DnD建立在HTML5拖放API之上。这是一个合理的默认值,因为它可以对已拖动的DOM节点进行屏幕快照,并将其用作开箱即用的“拖动预览”。方便的是,您不必在光标移动时进行任何绘制-这尼玛就是坑,为了这一个小的牺牲很大的哦
3 .它在触摸屏上不起作用,并且与其他浏览器相比,它在IE上提供的自定义机会更少
4 .所有后端都将DOM事件转换为React DnD可以处理的内部Redux动作
5 .拖拽和被放置区域都有类型。这些类型使您可以指定兼容哪些拖动源和放置目标
6 .监控器

1 .拖放本质上是由状态的,正在进行拖动操作,或者没有,有当前类型和当前项目。或者没有
2 .所有的状态其实自己已经写好了,在collect里面收集就好了,选取自己需要的返回即可
3 .那为什么会没有当前拖放元素的位移呢。。。。

7 .连接器

1 .

最简单使用

1 .父组件:最外面包起来

import { HTML5Backend } from 'react-dnd-html5-backend'
import { DndProvider } from 'react-dnd'

ReactDOM.render(
    <DndProvider backend={HTML5Backend}>
      <PageTwo/>
      <Drop/>
    </DndProvider>,
  document.getElementById('root')
);

2 .可拖拽的组件

import React, { CSSProperties } from 'react';
import { useDrag } from 'react-dnd';

const style: CSSProperties = {
    width: 200,
    height: 50,
    lineHeight: '50px',
    background: 'pink',
    margin: '30px auto'
}

const Box = () => {
    const [, drager] = useDrag({
        item: { type: 'Box' }
    })
    return (
        <div style={ style } ref={drager}>可拖拽组件 Box</div>
    )
}

export default Box;

放置拖拽的区域

import React, { CSSProperties } from 'react';
import { useDrop, DropTargetMonitor } from 'react-dnd';

const style: CSSProperties = {
    width: 400,
    height: 400,
    margin: '100px auto',
    lineHeight: '60px',
    border: '1px dashed black'
}

const Dustbin = () => {
    // 第一个参数是 collect 方法返回的对象,第二个参数是一个 ref 值,赋值给 drop 元素
    const [collectProps, droper] = useDrop({
        // accept 是一个标识,需要和对应的 drag 元素中 item 的 type 值一致,否则不能感应
        accept: 'Box',
        // collect 函数,返回的对象会成为 useDrop 的第一个参数,可以在组件中直接进行使用
        collect: (minoter: DropTargetMonitor) => ({
            isOver: minoter.isOver()
        })
    })
    const bg = collectProps.isOver ? 'deeppink' : 'white';
    const content = collectProps.isOver ? '快松开,放到碗里来' : '将 Box 组件拖动到这里'
    return (
        // 将 droper 赋值给对应元素的 ref
        <div ref={ droper } style={{ ...style, background: bg }}>{ content }</div>
    )
}

export default Dustbin;

数据拖拽放入几个区域

box
import React from 'react';
import { useDrag } from 'react-dnd';

let listId=100000000000
// 默认的增加id其实应该是原来数组的长度+1,这里先写一个很长的,之后在尝试修改吧

//  在他这里:所有拖拽的改变数据的操作都是在数据释放的时候操作的,并不是在接受拖拽物体释放的容器上面
// 那感觉这里有点不太科学,把所有容器之间的数据流动全都绑定到一个box上面,那么就意味着一个box每多一个可以拖放的地方,就要多传一个数据和方法,这样感觉效率非常的低下。
// 还是应该在拖放区域检测真正的数据变化。
// from,index
// 在任意一个可释放拖拽的区域,只要知道即将拖拽的元素的来自的数组和在数组内的位置,就可以通过改变数组来实现视图的变化
//接下来就是另一种写法,处理数据的时候都是在拖拽区域收到有东西在这里释放的时候操作
const style = {
    border: '1px dashed gray',
    backgroundColor: 'white',
    padding: '0.5rem 1rem',
    marginRight: '1.5rem',
    marginBottom: '1.5rem',
    cursor: 'move',
    width:'100px'
    // float: 'left',
}

export interface ListData{
    id:number,
    category:string,
    bg:string,
    type:string
}

export interface ListProps{
    oldCardList:ListData[],
    newCardList:ListData[],
    changeOldCardList:(list:ListData[])=>void,
    changeNewCardList:(list:ListData[])=>void,
}
const Box=({category,bg,oldCardList,newCardList,type,changeOldCardList,changeNewCardList}:ListData&ListProps)=> {
    const [{isDragging}, drager] = useDrag({
        item: { type:type,name:category},
        begin:()=>{
                // 开始拖拽的时候,新数组来一个占位符
                const useless=newCardList.find((item)=>item.id===-1)
                if(!useless){
                    // changeNewCardList(
                    //     [{
                    //     bg:"aqua",
                    //     category:"放这里",
                    //     id:-1,
                    //     },
                    //     ...newCardList]
                    // )
                    // 通过这种方法好像可以避免再次重新赋值
                    // 这里不是要加一个数据进去,直接给所有可释放区域一个标记就好了
                }        
        },
        end:(item,monitor)=>{
            const newUselessIndex=newCardList.findIndex((v)=>{
                if(item){
                   return v.category===item.name
                }
            })
            const oldUseLessIndex=oldCardList.findIndex((v)=>{
                if(item){
                    return v.category===item.name
                }
            })
            const result=monitor.getDropResult()
            // 拖拽放入的父容器的名字和行为

            // 拖拽结束,判断是否将拖拽元素放入了目标接收组
            // 如果是,用传入的元素代替占位元素
            // 如果否,将占位元素删除
            if(item&&result){
                // item就是上面的item
                // result 主要有以下参数
                // dropeffect:move
                // name:
                // 这个name是容器的drop方法返回的name
                // 如果在 drop target 的 drop 方法中返回了一个对象,在这里可以通过 monitor.getDropResult() 获取到返回结果
                // console.log(`dropped  ${item.name}  into  ${result.name}`)
            }
            if(monitor.didDrop()&&result){
                // 拖放的时候检查是否有之前的
                // if(newCardList.find((item)=>item.name===monitor.getItem().name)){

                // }
                newCardList.push(oldCardList[oldUseLessIndex])
                //为新的数组添加拖拽过去的元素
                oldCardList.splice(oldUseLessIndex,1)
            }
            changeOldCardList(oldCardList)
        },
        collect:(monitor)=>{
            return {
                isDragging:monitor.isDragging()
            }
        }
    })
    const opacity=isDragging?0.4:1
    const backgroundColor=bg
    return (
    <div style={{...style,opacity,backgroundColor}} ref={drager}>{category}</div>
    )
}

export default Box;



import React, { CSSProperties } from 'react';
import { useDrop, DropTargetMonitor } from 'react-dnd';

const style: CSSProperties = {
    width: 100,
    height: 100,
    margin: '10px auto',
    lineHeight: '60px',
    border: '1px dashed black'
}
interface DustbinOptions{
    accept:string
    // 可以接受的传入数据
    allowedDropEffect?:string,
    // 接受拖拽的类型,移动,复制,或者任何类型
    name:string,
}
// 这里必须这样写...

const Dustbin = ({accept,allowedDropEffect}:DustbinOptions) => {
    // 第一个参数是 collect 方法返回的对象,第二个参数是一个 ref 值,赋值给 drop 元素
    const [collectProps, droper] = useDrop({
        // accept 是一个标识,需要和对应的 drag 元素中 item 的 type 值一致,否则不能感应
        accept: accept,
        // collect 函数,返回的对象会成为 useDrop 的第一个参数,可以在组件中直接进行使用
        collect: (minoter: DropTargetMonitor) => ({
            isOver: minoter.isOver(),
            canDrop:minoter.canDrop(),
        }),
        
        hover:(e)=>{
            // console.log('有东西上来了')
        },
        drop:(e,ac)=>{
            return {
                name:"Dustbin",
                allowedDropEffect,
            }
        }
    })
    // console.log('drop props',collectProps)
    const isActive=collectProps.canDrop&&collectProps.isOver
    const bg = isActive ? 'deeppink' : 'white';
    const content = isActive ? '快松开,放到碗里来' : `将${accept}组件拖动到这里`
    return (
        // 将 droper 赋值给对应元素的 ref
        <div ref={ droper } style={{ ...style, background: bg }}>{ content }</div>
    )
}

export default Dustbin;


import React, { useState } from 'react';
import Box, { ListData } from './box';
import Dustbin from './Dustbin'
let arrlist=[ 
    { id: 1, category: 'Apple-1', bg: 'red',type:"box1"},
    { id: 2, category: 'Banana-2', bg: 'yellow',type:"box2"},
    { id: 3, category: 'Orange-1', bg: 'orange',type:"box1"},
    { id: 4, category: 'Grape-1', bg: 'purple',type:"box1"},
    { id: 5, category: 'Watermelon-2', bg: 'green',type:"box2"},
    { id: 6, category: 'Peach-1', bg: 'pink',type:"box1"},
]
const Container=()=>{
    const [newCardList,setNewCardList]=useState<ListData[]>([])
    const changeNewCardList=(list:ListData[])=>{
        setNewCardList([...list])
    }

    const [oldCardList,setOldCardList]=useState<ListData[]>(arrlist)
    const changeOldCardList=(list:ListData[])=>{
        setOldCardList([...list])
        // 这样就能保证每次都是新的值,有点秒
    }
    return (
        <div>
            {
             arrlist.map((item)=><Box 
                                        {...item} 
                                        newCardList={newCardList}
                                        oldCardList={arrlist}
                                        key={item.id}
                                        type={item.type}
                                        changeNewCardList={changeNewCardList}
                                        changeOldCardList={changeOldCardList}
                                 />)
                    // 还能这样传入参数吗?直接一个括号把所有的一股脑塞进去,好像后面的style也有这样的用法,其实就是解构对象然后传进去   
            }
            {
                newCardList.map((e)=>{
                return <div key={e.id}>{e.category}</div>
                    }
                )
            }
            <Dustbin accept="box1" allowedDropEffect="move" name="b1"></Dustbin>
            <Dustbin accept="box2" allowedDropEffect="copy" name="b2"></Dustbin>
        </div>
    )
}
export default Container

他这里完全不能实现改变拖拽物体的鼠标样式

1 .没有一个接口可以在拖拽的时候移动被拖拽的物体,实际上移动的仅仅是被拖拽物体的快照。
2 .如果想要不出现手只能把整个界面变成可释放拖拽区域,这样在拖拽的时候就不会出现那个手了。这一点react-beautiful-dnd就实现的很好

相关文章

网友评论

      本文标题:react-dnd 使用

      本文链接:https://www.haomeiwen.com/subject/splujktx.html