react-dnd 使用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react-dnd 使用相关的知识,希望对你有一定的参考价值。

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

7 .连接器

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

2 .可拖拽的组件

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

react-dnd 的 connectDragPreview() 是如何工作的?

【中文标题】react-dnd 的 connectDragPreview() 是如何工作的?【英文标题】:How does react-dnd's connectDragPreview() work? 【发布时间】:2018-07-26 10:35:49 【问题描述】:

我一直在查看文档和 github 问题,connectDragPreview 对我来说仍然是个谜。我想为项目在被拖动时的显示方式定义一个自定义预览。它的功能应该与示例 here 与拖动时出现的马图像一起使用。这是我的代码:

export const tokenCollector: DragSourceCollector = (connect, monitor) => 
  return 
    connectDragSource: connect.dragSource(),
    connectDragPreview: connect.dragPreview(),
    isDragging: monitor.isDragging()
  ;
;

class TokenClass extends React.Component<TokenProps> 
  componentDidMount() 
    const  connectDragPreview  = this.props;
    const img = new Image();
    img.src = '<encoded image>';
    img.onload = () => connectDragPreview(<div>img</div>);
  
  render() 
    const  connectDragSource, isDragging, children  = this.props;
    return connectDragSource(
      <div style= opacity: isDragging ? 0.5 : 1 >
        children
      </div>
    );
  

const dragSource = DragSource(DropType.Token, tokenSpec, tokenCollector)(TokenClass);
export  dragSource as Token ;

标准预览与此代码一起出现。

然后我尝试使用connectDragPreview 将我的connectDragSource 包装在我的组件的render() 方法中,但这似乎只会更改从中拾取它的拖动源,而不是它被拖动时的显示方式。

如何指定应用作拖动视觉对象的标记?

【问题讨论】:

留下了答案。如果这不起作用,你能分享你的DropType.TokentokenSpec吗? 【参考方案1】:

尝试将您的img.onload 更改为直接使用img,而不是将其包装在div 中:

img.onload = () => connectDragPreview(img);

据我所知,这是您的代码与示例的唯一真正区别。

【讨论】:

【参考方案2】:

看来您正在使用 react-dnd-html5-backend。 React-dnd-html5-backend 提供connectDragSource:https://github.com/react-dnd/react-dnd-html5-backend/blob/85fc956c58a5d1a9fde2fca3c7fca9115a7c87df/src/HTML5Backend.js#L100

并且 react-dnd-html5-backend 仅适用于 html5 拖放事件:https://github.com/react-dnd/react-dnd-html5-backend/blob/85fc956c58a5d1a9fde2fca3c7fca9115a7c87df/src/HTML5Backend.js#L65-L74

所以,你可以只设置一个Image来拖动效果:https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/setDragImage

您可以简单地更改您的 img.onload 回调:

export const tokenCollector: DragSourceCollector = (connect, monitor) => 
  return 
    connectDragSource: connect.dragSource(),
    connectDragPreview: connect.dragPreview(),
    isDragging: monitor.isDragging()
  ;
;

class TokenClass extends React.Component<TokenProps> 
  componentDidMount() 
    const  connectDragPreview  = this.props;
    const img = new Image();
    img.src = 'base64-image';
    img.onload = () => connectDragPreview(img);
  
  render() 
    const  connectDragSource, isDragging, children  = this.props;
    return connectDragSource(
      <div style= opacity: isDragging ? 0.5 : 1 >
        children
      </div>
    );
  

const dragSource = DragSource(DropType.Token, tokenSpec, tokenCollector)(TokenClass);
export  dragSource as Token ;

如果您想使用自定义标记,可以使用 getEmptyImage 参数调用 connectDragPreview,然后实现 CustomDragLayer。

你的 TokenClass:

import React,  Component  from "react";
import  DragSource  from "react-dnd";
import logo from "./logo.svg";
import  getEmptyImage  from "react-dnd-html5-backend";

export const tokenCollector = (connect, monitor) => 
  return 
    connectDragSource: connect.dragSource(),
    connectDragPreview: connect.dragPreview(),
    isDragging: monitor.isDragging()
  ;
;

class TokenClass extends React.Component 
  componentDidMount() 
    const  connectDragPreview  = this.props;

    // Use empty image as a drag preview so browsers don't draw it
    // and we can draw whatever we want on the custom drag layer instead.
    connectDragPreview(getEmptyImage());
  
  render() 
    const  connectDragSource, isDragging, children  = this.props;
    return connectDragSource(
      <div style= opacity: isDragging ? 0.5 : 1, backgroundColor: "green" >
        children
      </div>
    );
  


const tokenSpec = 
  beginDrag() 
    return ;
  
;

export default DragSource("DropType.Markup", tokenSpec, tokenCollector)(
  TokenClass
);

CustomDragLayer 实现:

import React,  Component  from "react";
import  DragLayer  from "react-dnd";

function getItemStyles(props) 
  const  initialOffset, currentOffset  = props;
  if (!initialOffset || !currentOffset) 
    return 
      display: "none"
    ;
  

  let  x, y  = currentOffset;

  const transform = `translate($xpx, $ypx)`;
  return 
    transform,
    WebkitTransform: transform
  ;


class CustomDragLayer extends Component 
  render() 
    const isDragging = this.props.isDragging;
    if (!isDragging) 
      return null;
    

    // You can specify acceptable type:
    if (this.props.itemType !== "DropType.Markup") 
      return null;
    

    // The component will work only when dragging
    return (
      <div>
        <div style=getItemStyles(this.props)>Custom drag layer!</div>
      </div>
    );
  


function collect(monitor) 
  return 
    item: monitor.getItem(),
    itemType: monitor.getItemType(),
    initialOffset: monitor.getInitialSourceClientOffset(),
    currentOffset: monitor.getSourceClientOffset(),
    isDragging: monitor.isDragging()
  ;


export default DragLayer(collect)(CustomDragLayer); // eslint-disable-line new-cap

另外,我上传到 github 解决方案,该解决方案适用于图像和标记。您可以上传它们并运行: yarn install yarn start 解决方案:https://github.com/xnimorz/***-example/tree/dragAndDrop

【讨论】:

connectDragPreview 是不接受图像的ConnectDragPreview 类型。您的代码出现以下错误:Argument of type 'HTMLImageElement' is not assignable to parameter of type 'ReactElement&lt;&gt;'. FWIW,我在此示例中使用图像的唯一原因是为了匹配 React DnD 的教程。最终,我需要指定标记,所以我的 &lt;div&gt; 示例是恰当的。 @im1dermike DragAndDrop react-dnd-html5-backend 功能在 connectDragPreview 方法中默认不支持自定义标记。要设置自定义标记,您可以实现 CustomDragLayer。我编辑了我的答案并添加了指向带有图像和标记的解决方案的链接 (github.com/xnimorz/***-example/tree/dragAndDrop) 非常感谢您对此问题的贡献。我想我越来越近了。我实现了您建议的代码 (kopy.io/HWRqn),但现在当我拖动时,我什么也看不到。我错过了什么? 确保在使用 DragDropContext 包装的组件中使用 CustomDragLayer。 github.com/xnimorz/***-example/blob/dragAndDrop/src/… 并且避免在 onMouseEvent 和 onMouseLeave 方法中使用 preventDefault 函数 @Julka 它取决于拖放上下文。 React-dnd 支持特殊的中间件(称为后端),它应该实现特定的 dnd 逻辑,例如react-dnd-html5-backend 适用于“拖动”和“放下”事件或触摸后端react-dnd.github.io/react-dnd/docs/backends/touch-backend。有关后端的更多信息,您可以在此处找到:react-dnd.github.io/react-dnd/docs/api/drag-drop-context【参考方案3】:

import React from 'react';
import  DragSource  from 'react-dnd';

/* ... */

function collect(connect, monitor) 
  return 
    connectDragSource: connect.dragSource(),
    connectDragPreview: connect.dragPreview()
  ;


class ComponentWithCopyEffect 
  render() 
    const  connectDragSource  = this.props;

    return connectDragSource(
      <div>
        This div shows a plus icon in some browsers.
      </div>,
       dropEffect: 'copy' 
    );
  
);
ComponentWithCopyEffect = DragSource(/* ... */)(ComponentWithCopyEffect);

class ComponentWithHandle 
  render() 
    const  connectDragSource, connectDragPreview  = this.props;

    return connectDragPreview(
      <div>
        This div is draggable by a handle!
        connectDragSource(
          <div>drag me</div>
        )
      </div>
    );
  

ComponentWithHandle = DragSource(/* ... */)(ComponentWithHandle);

class ComponentWithImagePreview 
  componentDidMount() 
    const  connectDragPreview  = this.props;

    const img = new Image();
    img.src = '/image.jpg';
    img.onload = () => connectDragPreview(img);
  

  render() 
    const  connectDragSource  = this.props;

    return connectDragSource(
      <div>
        This div shows an image when dragged!
      </div>
    );
  

ComponentWithImagePreview = DragSource(/* ... */)(ComponentWithImagePreview);

Source

【讨论】:

以上是关于react-dnd 使用的主要内容,如果未能解决你的问题,请参考以下文章

使用 ReactJs 和 react-dnd 拖放可排序列表的问题

react-dnd使用介绍

看不到我使用 React-dnd 拖动的项目

react-dnd 使用

react-dnd 中的动画

如何实现 react-dnd useDragLayer?