react-dnd 使用

优势

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就实现的很好

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343