我创建了一个DropTarget,并按如下所示连接了getClientOffset:
targetCollect = function (connect, monitor) {
return {
connectDropTarget: connect.dropTarget(),
isOver: monitor.isOver(),
clientOffset: monitor.getClientOffset()
}
}
但是,在组件内移动拖动时,目标不会随着鼠标的每一次摆动而收到更新的道具(仅在进入和退出时)。 但是,目标规范的悬停_is_反复调用。 是否存在每次更改后都不注入clientOffset道具的原因?
哦,好点。 没错,我没想到这种用法。 当前,唯一选择跟踪客户端偏移量的方法是使用DragLayer
。 否则,出于性能原因,仅当与_drop目标本身有关的某些内容发生更改时,才更新道具。
我会说这是一个API问题,可能有几种解决方案:
collect
函数内部进入getClientOffset()
和类似方法,并告诉使用DragLayer
代替(简单但愚蠢);我想我会接受PR做第二种选择。 您可以检查DragLayer
实现,以了解它如何订阅偏移量更改。
我研究了第二个选项,但是它很棘手,因为collect是一个函数,因此不可能/不容易发现可能调用了哪些监视函数。
我的用例是拖动到适当位置的小部件,可能以相对复杂的方式替换其他小部件(例如在Android上拖动程序图标)。 实际上,必须根据确切的悬停位置重新排列目标,而拖动本身作为HTML5快照完全可以。 我可以通过目标规范的悬停来更新目标组件的状态。 因此,我暂时将其保留并解决它。 谢谢你的图书馆! 在几个小时的工作中,我的工作就不错。
让我们暂时保持打开状态。 与#172相同:我没想到人们会在canDrop
或收集函数中使用增量。 我不会立即解决此问题,但可能会在一个月左右的时间里释放出来。
我也遇到了这个问题。 您对让客户将标志传递到DropTarget规范中以订阅偏移更改有何想法? 对于用户来说,这是另外一个“陷阱”,但是比手动订阅/取消订阅偏移量要简单。
您是否可以不使用RxJS-DOM来观察元素上的拖动事件并使用鼠标坐标来更新状态? 哈克解决方案。
componentDidMount() {
this._events.dragOver = DOM.fromEvent(findDOMNode(this.refs.grid), 'dragover')
.throttle(200)
.subscribe(this.getMouseCoords)
}
我最终抓住了显示器,订阅了像DragLayer这样的偏移更改。 我还最终进入了一些地方的内部注册表。 我不认为我想做的也很寻常,因此IMO可以通过简单的api更改来解决。
有什么事吗我想在DropTarget而不是自定义拖动层中监视客户端偏移,因为当我可以从DropTarget而不是自定义拖动层访问悬停位置时,我觉得更改DropTarget的结构更容易。
+1我想做@jchristman想要做的事情。
+1。 遇到这个问题并浪费大量时间试图理解为什么getClientOffset()在canDrop()中可以正常工作-确实会被反复调用...-但是无法将控件转发到我所在项目的“位置”。 我需要能够在treenode的上方和下方插入,而不仅仅是“插入”它。 canDrop()无法找到让我实际告诉组件节点具有“滴”的许多“类型”中的哪一个类型的方法,以便控件可以相应地重新显示。 在canDrop中更改prop会导致错误。 订阅onMouseMove在拖放操作期间不起作用。 到目前为止,这一直是浪费时间……任何帮助将不胜感激。
@ibash ,您介意发布解决问题的要点吗?
最终切换到使用悬停功能对我有用。
`悬停:(道具:MyProps,监视器:DropTargetMonitor,组件:ReportItemRow)=> {
if(component!= null){
const clientOffset = monitor.getClientOffset();
// Is the dragging node dragged above/below us as opposed to "on" us?
const elementRect = document.elementFromPoint(clientOffset.x, clientOffset.y).getBoundingClientRect();
const percentageY = (clientOffset.y - elementRect.top) / elementRect.height;
// Divide the box up into .25 .5 .25
const insertDragAndDropMagnetPercent = .25;
if (insertDragAndDropMagnetPercent >= percentageY) {
component.setState({ dropTarget: "above" });
}
else if (1 - insertDragAndDropMagnetPercent >= percentageY) {
component.setState({ dropTarget: "inside" });
}
else {
component.setState({ dropTarget: "below" });
}
}
},`
@ noah79 ,与此同时,您可以通过创建多个放置目标并将其放置在treenodes下来解决问题。 像这样的东西:
_____
_____| <-- drop div1 (above treenode1/below top of list)
treenode1_____| <-- drop div2 (on treenode1)
_____| <-- drop div3 (below treenode1/above treenode2)
treenode2_____| <-- drop div4 (on treenode 2)
_____| <-- drop div5 (below treenode2/above bottom of list)
最后得到2n + 1个放置目标,其中n是列表中元素的数量。 然后,基于将鼠标悬停在哪个div上,您可以更改树形列表的外观。 我已经执行了与此非常相似的操作来解决暂时无法访问getClientOffset()的问题,并且我没有注意到性能受到影响。 每个drop div的高度应为treenode1线的高度的1/2,而第一个drop div的高度应比treenode1线的高度高出第一行的1/4。 也就是说,您的CSS应该如下所示:
.dropdiv-container {
position: absolute;
top: -0.25em; /* If the top of this container is aligned with the top of the treenode list initially */
}
.dropdiv {
height: 0.5em; /* If the height of treenode1 line is 1em with no padding */
}
<div className='dropdiv-container'>
{ renderDropTargets(2 * listLength + 1) }
</div>
那有意义吗?
这是我如何解决此问题的要点。 诀窍是只触及
react-dnd内部,并直接使用全局监视器。 看看
DragDropMonitor.js源代码,以了解可用的方法。
参考: https :
您必须原谅咖啡脚本:)
# in the component being dragged, get access to the dragDropManager by adding
# it to the contextTypes
#
# dragDropManager is an instance of this:
# https://github.com/gaearon/dnd-core/blob/master/src/DragDropManager.js
<strong i="11">@contextTypes</strong>: {
dragDropManager: React.PropTypes.object
}
# because we can receive events after the component is unmounted, we need to
# keep track of whether the component is mounted manually.
#
# <strong i="12">@_monitor</strong> is what lets us access all the nice internal state - it is an instance of this:
# https://github.com/gaearon/dnd-core/blob/master/src/DragDropMonitor.js
componentWillMount: () =>
<strong i="13">@_isMounted</strong> = true
<strong i="14">@_monitor</strong> = @context.dragDropManager.getMonitor()
<strong i="15">@_unsubscribeToStateChange</strong> = @_monitor.subscribeToStateChange(@_onStateChange)
<strong i="16">@_unsubscribeToOffsetChange</strong> = @_monitor.subscribeToOffsetChange(@_onOffsetChange)
componentWillUnmount: () =>
<strong i="17">@_isMounted</strong> = false
<strong i="18">@_monitor</strong> = null
@_unsubscribeToStateChange()
@_unsubscribeToOffsetChange()
# we can access dragging / dropping state by accessing the monitor
_onStateChange: () =>
return unless <strong i="19">@_isMounted</strong>
# When we stop dragging reset the counter state and hide all cursors.
if <strong i="20">@_previousIsDragging</strong> && !@_monitor.isDragging()
console.log('no longer dragging')
<strong i="21">@_previousIsDragging</strong> = @_monitor.isDragging()
_onOffsetChange: () =>
return unless <strong i="22">@_isMounted</strong>
# mouse is the x/y coordinates
mouse = @_monitor.getClientOffset()
# item is the drag item
item = @_monitor.getItem()
# if you want to check if a dragged item is over a target, you need the
# targetId -- in the DropTarget wrapper you can pass it in like:
#
# (connect, monitor) ->
# {
# targetId: monitor.targetId,
# connectDropTarget: connect.dropTarget()
# }
#
# and then you can use it like below
@_monitor.isOverTarget(@props.targetId))
这有意义吗? 如果没有,我可以添加更多详细信息
我今天也遇到了这个问题,浪费了几个小时。 在DropTarget-> The Collecting Function下的文档中留下关于此的注释该怎么办? 至少这将使其他人免于沮丧。
顺便说一句。 关于此问题的另一种非常丑陋的破解方法可以是从dropTarget.hover()
发送坐标_as state_:
const dropTarget = {
hover(props, monitor, component) {
// HACK! Since there is an open bug in react-dnd, making it impossible
// to get the current client offset through the collect function as the
// user moves the mouse, we do this awful hack and set the state (!!)
// on the component from here outside the component.
component.setState({
currentDragOffset: monitor.getClientOffset(),
});
},
drop() { /* ... */ },
};
当然,有很多更正确的方法可以避免这样的setState滥用。 但是至少这个小技巧非常精简,并且可以解决这个问题,直到最终解决此问题为止。 它使您可以解决该错误,而无需更改其他组件和/或文件,也无需依赖库内部。
编辑:这基本上与noah79相同,我只是之前没有读过他的代码。
真的没有办法让鼠标放下吗? 我的目标组件不需要了解它,因此setState
不是我的选择。 我只需要坐标即可触发redux动作。
请忽略我的最后一条消息。 我现在看到drop
和endDrag
是两个不同的功能,并且使光标定位在drop
可以按预期工作。 :)
根据我的测试,将项目拖到目标上方时,react-dnd会消耗鼠标移动事件。 如果html后端可能不使用此事件,则当isOver属性设置为true时,目标组件可以有条件地将一个普通的mousemove侦听器放置在其dom上。 此后,当在其上拖动某些内容时,此侦听器可以按正常方式拾取鼠标位置。 我使用drag()方法中的setState使其暂时起作用,react是在渲染过渡的中间发出有关设置状态的警告。
这也把我绊倒了。 +1以获得API解决方案,或者至少获得FAQ上的内容。
有什么计划支持这一点吗?
我有一个无黑客的解决方法的想法:为DropTarget
制作一个包装器,该包装器还包装hover
来触发对收集函数的另一个调用,如果收集函数返回新值,则重新渲染。
为此问题+1。 我的用例是,我有几个彼此接近的放置目标。 悬停时,适当的放下目标会突出显示,但实际上放下会下降到较低的目标,这取决于您悬停的方向,这似乎很奇怪。
我偶然发现了这个问题,研究为什么偏移量没有更新,似乎还需要一种解决方法。
我接受了@ jedwards1211的想法,这就是我的想法。 HOC具有与香草DropTarget
相同的接口,但在其选项中接受collectRapidly
,这是通过收集函数查询的道具列表,并在每个悬停事件上传递。 从收集函数中不加选择地传递所有道具会引起一些怪异,并且我们根本无法将connect
传递给收集器,因此要防止“快速”查询connectDropTarget
。
因此,您可以使用RapidFireDropTarget(types, target, collect, {collectRapidly: ['offset']})
而不是DropTarget(types, target, collect)
,其中offset
大概是从monitor.getOffset
函数系列接收其值的东西。
import React from 'react';
import {DropTarget} from 'react-dnd';
import pick from 'lodash/pick';
function RapidFireDropTarget(types, spec, collect, options={}) {
let collectProps = {};
const prevHover = spec.hover;
const {collectRapidly = []} = options;
const dummyConnect = {
dropTarget: () => () => {
throw new Error('Rapidly collected props cannot include results from `connect`');
}
};
const WrappedComponent = Component => {
return class extends React.Component {
render() {
return <Component {...this.props} {...collectProps} />;
}
};
};
spec.hover = (props, monitor, component) => {
const allCollectProps = collect(dummyConnect, monitor);
collectProps = pick(allCollectProps, collectRapidly);
component.forceUpdate();
if (prevHover instanceof Function) {
prevHover(props, monitor, component);
}
}
return (Component) => {
return DropTarget(types, spec, collect, options)(WrappedComponent(Component));
};
}
export default RapidFireDropTarget;
由于此问题最近没有活动,因此已被自动标记为陈旧。 如果没有进一步的活动,它将关闭。 感谢您的贡献。
最有用的评论
+1。 遇到这个问题并浪费大量时间试图理解为什么getClientOffset()在canDrop()中可以正常工作-确实会被反复调用...-但是无法将控件转发到我所在项目的“位置”。 我需要能够在treenode的上方和下方插入,而不仅仅是“插入”它。 canDrop()无法找到让我实际告诉组件节点具有“滴”的许多“类型”中的哪一个类型的方法,以便控件可以相应地重新显示。 在canDrop中更改prop会导致错误。 订阅onMouseMove在拖放操作期间不起作用。 到目前为止,这一直是浪费时间……任何帮助将不胜感激。
@ibash ,您介意发布解决问题的要点吗?