react-dnd 的 connectDragPreview() 是如何工作的?
Posted
技术标签:
【中文标题】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.Token
和tokenSpec
吗?
【参考方案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<>'.
FWIW,我在此示例中使用图像的唯一原因是为了匹配 React DnD 的教程。最终,我需要指定标记,所以我的 <div>
示例是恰当的。
@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 的 connectDragPreview() 是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章