react按钮拖拽_如何用React开发拖拽组件

拖拽组件是在前端开发中十分常见的一个功能,现在无论你是使用React还是Vue,都有很多现成的拖拽组件可以使用。不过,有些时候你可能还是需要自己去实现,那么就必须需要理解其实现原理。接下来这篇文章,我将详细介绍如何使用React框架来实现一个拖拽组件。

理解HTML5 拖放API

现如今,大部分的前端拖拽组件都依托于HTML5原生提供的拖放接口。那么在开始用具体框架来封装组件的之前,就需要搞清楚这些原生的接口功能。

HTML 5的DOM鼠标事件中添加了drag这个事件,对于一个设置了draggable的页面元素来说,只要将其拖动到一个droppable的元素上,就算完成了一次完整的拖放功能。在这一过程中,会分别触发一些如下事件类型:

事件类型

事件处理函数

含义drag

ondrag

拖放进行中

dragend/dragstart

ondragend/ondragstart

开始拖放和结束拖放

dragover

ondragover

当元素或选中的文本被拖到一个目标目标上(每100毫秒触发一次)。

dragenter/dragleave

ondragenter/ondragleave

源对象开始进入/离开目标对象范围内

drop

ondrop

源对象被拖放到目标对象上

熟悉这些基本事件类型后,实现上就是在源对象和目标对象上分别绑定对应的事件处理函数,并监听处理即可。

除了这些拖放的事件接口外,我们通常还需要处理数据的传递。HTML5中同样提供了简便的接口,在对应的监听函数内,我们可以拿到event对象,在这个对象内部有个DataTransfer接口,可专门用来保存事件的数据内容。对应的接口有:

event.dataTransfer.setData: 添加拖拽数据,这个方法接收两个参数,第一个参数是数据类型(可自定义),第二个参数是对应的数据

event.dataTransfer.getData:反向操作,获取数据,只接收一个参数,即数据类型

event.dataTransfer.clearData: 清除数据

event.dataTransfer.setDragImage: 可自定义拖放过程中鼠标旁边的图像

event.dataTransfer.effectAllowed: 指定拖放操作所允许的一个效果,有多个属性值,如link, move等,具体可参考https://developer.mozilla.org/zh-CN/docs/Web/API/DataTransfer/effectAllowed

了解完这些基本接口后,我们就可以着手使用React来编写自己的拖放组件了:

实现Drag组件

我们第一个要实现是Drag组件,它会作为我们的源对象,它的子组件都可以进行拖动。就像这样:

这个组件可以拖动

我们先来实现最基础的功能,通过setData接口来传递数据:

const Drag = (props) => {

const startDrag = ev => {

// 传输数据

ev.dataTransfer.setData("drag-item", props.dataItem);

};

return (

{props.children}

);

}

实现Drop组件

接着我们就要来实现目标组件了,需要定义一个对外暴露的接口用来接收拖拽完成后的事件:

请将组件拖放到这里

从实现上来说,监听onDragOver和onDrop这两个事件就可以了:

const DropTarget = (props) => {

const dragOver = ev => {

ev.preventDefault();

}

const drop = ev => {

// 获取数据

const droppedItem = ev.dataTransfer.getData("drag-item");

if (droppedItem) {

// 触发回调函数

props.onItemDropped(droppedItem);

}

}

return (

{props.children}

)

}

添加拖放效果

要实现拖放的视觉效果,需要effectAllowed和dropEffect两个属性结合起来使用。

先在Drag组件上设置effectAllowed属性:

const Drag = (props) => {

const startDrag = ev => {

ev.dataTransfer.setData("drag-item", props.dataItem);

// 添加效果

ev.dataTransfer.effectAllowed = props.dropEffect;

};

return (

{props.children}

);

}

接着我们设置一些效果常量:

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";

然后在目标组件上,我们通过给dropEffect属性赋值来引用这些效果常量,修改代码如下:

const DropTarget = (props) => {

const dragOver = ev => {

ev.preventDefault();

// 添加效果

ev.dataTransfer.dropEffect = props.dropEffect;

}

const dragEnter = ev => {

ev.dataTransfer.dropEffect = props.dropEffect;

}

const drop = ev => {

const droppedItem = ev.dataTransfer.getData("drag-item");

if (droppedItem) {

props.onItemDropped(droppedItem);

}

}

return (

{props.children}

)

}

Drag.defaultProps = {

dropEffect: dropEffects.All, // 设置默认的效果

};

进一步完善

到这一步,大体的功能我们都完成的七七八八了,最后还剩下一些收尾的工作。首先我们可以添加接口用来让用户可以自定义拖拽图像:

const Drag = (props) => {

const image = React.useRef(null);

React.useEffect(() => {

image.current = null;

if (props.dragImage) {

image.current = new Image();

image.current.src = props.dragImage;

}

}, [props.dragImage]);

const startDrag = ev => {

ev.dataTransfer.setData("drag-item", props.dataItem);

ev.dataTransfer.effectAllowed = props.dropEffect;

// 设置图片

if (image.current) {

ev.dataTransfer.setDragImage(image.current, 0, 0);

}

};

return (

{props.children}

);

}

接着,我们再来添加样式:

// 样式

const draggingStyle = {

opacity: 0.25,

};

const Drag = props => {

const [isDragging, setIsDragging] = React.useState(false);

const image = React.useRef(null);

React.useEffect(() => {

image.current = null;

if (props.dragImage) {

image.current = new Image();

image.current.src = props.dragImage;

}

}, [props.dragImage]);

const startDrag = ev => {

setIsDragging(true);

ev.dataTransfer.setData("drag-item", props.dataItem);

ev.dataTransfer.effectAllowed = props.dropEffect;

if (image.current) {

ev.dataTransfer.setDragImage(image.current, 0, 0);

}

};

// 拖拽结束时,添加样式

const dragEnd = () => setIsDragging(false);

return (

{props.children}

);

};

最后,需要注意的是,如果需要处理移动端的兼容性,那么可以使用如下库:

参考资料

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值