React 组件中的拖放
介绍
开发人员在构建应用程序前端(Web 或其他)时经常被问到的一个问题是:“您能在其中添加拖放功能吗?”
本指南将使用组件组合来创建两个组件:一个用于为 React 组件添加拖动功能,另一个用于将 React 组件转变为拖放目标。
拖拽组件
要创建的第一个组件是拖动组件。此组件将是一个容器组件,可在其子组件上启用拖动。它将接受单个 prop dataItem作为被拖动数据的标识符,并将按如下方式使用:
<Drag dataItem="item-1">
<div>Something to be dragged</div>
</Drag>
该组件将在div元素内呈现子元素,如下所示:
<div>
{props.children}
</div>
要在组件上启用拖动功能,我们需要做两件事。首先,我们需要在元素上设置draggable属性,其次,处理onDragStart事件。在此处理程序中,我们应该调用event.dataTransfer.setData来设置可用于放置目标的数据,以识别已放置的内容。在此组件中,数据将是作为dataItem属性传入的任何内容。
该组件现在应如下所示:
function startDrag(ev) {
ev.dataTransfer.setData("drag-item", props.dataItem);
}
return(
<div draggable onDragStart={startDrag}>
{props.children}
</div>);
现在可以使用dataTransfer对象上的拖拽项数据来拖动和识别子项。
放置目标组件
现在,我们有一个可以拖动但无处放置的组件,因此我们将创建一个放置目标组件。该组件将再次是一个容器组件,将其子项包装在div元素中;该组件将具有一个事件处理程序的单个 prop,当项目被放入其中时,该事件处理程序将被调用。
它将像这样被消耗:
<DropTarget onItemDropped={itemDropped}>
<div>...</div>
</DropTarget>
要启用组件上的放置功能,我们需要处理两个事件:onDragOver和onDrop。元素的默认拖拽行为是禁用放置,因此为了允许放置,处理程序需要通过调用event.preventDefault()来阻止此默认行为。现在将在组件上启用放置功能。放置事件的处理程序应调用event.dataTransfer.getData("drag-item")来检索要放置的项目的标识符,然后调用其onItemDropped处理程序。
放置目标组件将如下所示:
function dragOver(ev) {
ev.preventDefault();
}
function drop(ev) {
const droppedItem = ev.dataTransfer.getData("drag-item");
if (droppedItem) {
props.onItemDropped(droppedItem);
}
}
return (
<div onDragOver={dragOver} onDrop={drop}>
{props.children}
</div>);
现在可以将组件包裹在 Drag 组件中并将其放置在 DropTarget 组件中。
掉落效果
目前,放置目标允许将任何内容放置在其上,并且拖动光标始终相同。可以使用放置效果来控制这些效果。可用的效果包括复制、移动、链接以及它们的任意组合。使用开始拖动处理程序中的event.dataTransfer.effectAllowed设置被拖动对象的效果,使用拖动处理程序和拖动进入处理程序onDragEnter中的event.dataTransfer.dropEffect设置放置目标的效果。设置这些属性将具有更改拖动光标和控制是否可以将项目放置在特定目标上的效果。我们现在将扩展组件以实现放置效果。
首先,我们将向 Drag 组件添加一个名为dropEffect的可选字符串类型的 prop ,并在启动拖动处理程序中将 effectAllowed 设置为prop 的值。由于此 prop 是可选的,我们将使用defaultProps静态属性为其提供默认值“all”,这意味着它将支持所有三种效果。Drag 组件的主要部分现在如下所示:
function startDrag(ev) {
ev.dataTransfer.setData("drag-item", props.dataItem);
ev.dataTransfer.effectAllowed = props.dropEffect;
}
return(
<div draggable onDragStart={startDrag}>
{props.children}
</div>);
我们将向 DropTarget 组件添加一个相同的 prop,并在拖动输入和拖动处理程序中将dropEffect设置为 prop 的值。DropTarget 组件现在将如下所示:
function dragOver(ev) {
ev.preventDefault();
ev.dataTransfer.dropEffect = props.dropEffect;
}
function dragEnter(ev) {
ev.dataTransfer.dropEffect = props.dropEffect;
}
function drop(ev) {
...
}
return (
<div onDragOver={dragOver} onDragEnter={dragEnter} onDrop={drop}>
{props.children}
</div>);
放置效果的有效值为“copy”、“link”、“move”、“copyMove”、“copyLink”、“linkMove”、“all”和“none”。这些值的含义应该相当清楚,但为了帮助组件的使用者,应该将它们声明为常量,从而确保如果使用常量设置dropEffect属性,那么我们肯定使用的是有效值。在本指南的示例代码中,这些值在单独的模块中声明,如下所示:
export const All = "all";
export const Move = "move";
export const Copy = "copy";
export const Link = "link";
export const CopyOrMove = "copyMove";
export const CopyOrLink = "copyLink";
export const LinkOrMove = "linkMove";
export const None = "none";
因此,例如,假设Drag 组件上的dropEffect属性设置为dropEffects.Move,而 DropTarget 组件上的dropEffects.Copy则将被禁用。如果 DropTarget 设置为dropEffects.CopyOrMove,则 drop 将被启用。
改进组件
现在,我们已拥有功能齐全的拖放功能。但是,还有其他技术可用于改进界面。在本节中,我们将添加在拖动时显示的图像,并添加样式以指示何时拖动元素以及何时有目标可用。
拖动图像
目前,拖拽时显示的图像是浏览器的默认图像,通常是被拖拽元素的不透明副本。可以使用event.transferData.setDragImage函数将此图像设置为任何元素。
我们将使用图像作为 Drag 组件,并添加一个可选的字符串 prop dragImage来包含要显示的图像的来源。如果未设置此 prop,我们将使用默认值。为了确保图像在使用前已加载,我们将添加一个效果钩子,当图像 prop 发生更改时触发,它将创建图像并将其加载到 ref 中,如下所示:
const image = React.useRef(null);
React.useEffect(() => {
image.current = null;
if (props.dragImage) {
image.current = new Image();
image.current.src = props.dragImage;
}
}, [props.dragImage]);
然后可以在onDragStart处理程序中使用它,如下所示:
if (image.current) {
ev.dataTransfer.setDragImage(image.current, 0, 0);
}
结束、进入和离开事件
我们还可以在拖放操作中使用onDragEnd,onDragEnter和onDragLeave事件。
Drag 组件将处理拖动结束事件,以帮助向用户指示何时拖动特定元素。我们将添加一个isDragging状态,该状态将在渲染组件时使用,并在其为true时将不透明度设置为 0.25。此状态将在开始拖动处理程序中设置为true ,在结束拖动处理程序中设置为false;这样,当用户拖动元素时,它将显示为不透明,否则将显示为正常。
当被拖拽的项目首次进入元素时,会触发onDragEnter组件,而当项目离开元素时,会触发onDragLeave 组件。DropTarget 组件将添加isOver状态并处理这些事件,在进入时将其设置为true,在退出时将其设置为false。呈现此状态将控制背景颜色和不透明度,以向用户表明他们正在拖拽到目标上。
结论
React 中的组合模型允许我们编写单个可重复使用的组件,这些组件可以为任何组件添加拖放效果。 使用这些组件的示例应用程序可在此处找到。
免责声明:本内容来源于第三方作者授权、网友推荐或互联网整理,旨在为广大用户提供学习与参考之用。所有文本和图片版权归原创网站或作者本人所有,其观点并不代表本站立场。如有任何版权侵犯或转载不当之情况,请与我们取得联系,我们将尽快进行相关处理与修改。感谢您的理解与支持!
请先 登录后发表评论 ~