温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

React怎么结合Drag API实现拖拽效果

发布时间:2023-03-06 14:06:42 来源:亿速云 阅读:70 作者:iii 栏目:开发技术

本篇内容主要讲解“React怎么结合Drag API实现拖拽效果”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“React怎么结合Drag API实现拖拽效果”吧!

    认识拖拽

    鼠标拖拽是一个常见的交互场景,在这个熟悉的过程将会发生哪些事件?

    React怎么结合Drag API实现拖拽效果

    拖拽事件指用户通过鼠标(或其他指针设备)将元素移到一个新的位置上。拖拽过程涉及两个对象:被拖拽元素(上图中 A )和可释放目标(上图中 B )

    被拖拽元素

    默认情况下,图片、链接和文本是可拖动的。HTML5 在所有 HTML 元素上规定了一个 draggable 属性, 表示元素是否可以拖动。图片和链接的 draggable 属性自动被设置为 true,而其他所有元素此属性的默认值为 false。

    某个元素被拖动时,会依次触发以下事件:

    • ondragstart:拖动开始,当鼠标按下并且开始移动鼠标时,触发此事件;整个周期只触发一次;

    • ondrag:只要元素仍被拖拽,就会持续触发此事件;

    • ondragend:拖拽结束,当鼠标松开后,会触发此事件;整个周期只触发一次。

    可释放目标

    当把拖拽元素移动到一个有效的放置目标时,目标对象会触发以下事件:

    • ondragenter:只要一把拖拽元素移动到目标时,就会触发此事件;

    • ondragover:拖拽元素在目标中拖动时,会持续触发此事件;

    • ondragleaveondrop:拖拽元素离开目标时(没有在目标上放下),会触发ondragleave;当拖拽元素在目标放下(松开鼠标),则触发ondrop事件。

    目标元素默认是不能够被拖放的,即不会触发 ondrop 事件,可以通过在目标元素的 ondragover 事件中取消默认事件来解决此问题。

    生命周期

    React怎么结合Drag API实现拖拽效果

    拖拽操作中的数据传输

    除非数据受影响,否则简单的拖放并没有实际意义。为实现拖动操作中的数据传输,event 对象上暴露了 dataTransfer 对象,用于从被拖动元素向放置目标传递字符串数据。我们使用它来通知画布,当前需要渲染的组件是什么。

    dataTransfer 对象主要有两个方法:getData() 和 setData(),分别用来获取和存储值。setData()的第一个参数以及 getData()的唯一参数是一个字符串,表示要设置的数据类型:"text"或"URL"

    ???? 虽然这两种数据类型是 IE 最初引入的,但 HTML5 已经将其扩展为允许任何 MIME 类型。为向后 兼容,HTML5 还会继续支持"text"和"URL",但它们会分别被映射到"text/plain"和"text/uri-list”

    需要注意的是:存储在 dataTransfer 对象中的数据只能在放置事件中读取。如果没有在 ondrop 事件中取得这些数据,dataTransfer 对象就会被销毁,数据也会丢失。

    代码实现

    我在项目中使用 React 来实现,并且考虑到跨组件通信,我使用了 dva 来管理数据流。

    如何标记当前拖拽的元素?

    HTML5 支持的 data-x 属性,我们可以将当前组件的类型 Rectangle 赋值给它,这样处理和画布组件通信方便一些

    const Block = (props) => {
      const handleDragStart = (e: React.DragEvent<HTMLDivElement>) => {
        // 向拖拽数据中添加项目    
        e.dataTransfer.setData('text', e.target.dataset.index);
      };
      return (
        <div onDragStart={handleDragStart}>
          <Button draggable data-index="Rectangle">
            二维码
          </Button>
        </div>
      );
    };

    在上文中讲到,dataTransfer 的数据必须在 handleDrop 方法中获取。实际的用来保存画布中的所有组件的数据:

    function DragEditor(props) {
      const { dvaStore, dispatch } = props;
      // 阻止浏览器默认事件,否则 ondrop 不会触发
      const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
        e.preventDefault();
      };
      const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
        e.preventDefault();
        // 获取拖拽元素的组件类型
        const type = e.dataTransfer.getData('text');
        // COMPONENT_LIST 定义了组件的数据格式,根据 type 匹配
        const component = COMPONENT_LIST.filter(
          (i) => i.component === type,
        )[0];
        // 将组件数据添加到 store,画布将会根据数据渲染出组件
        if (component) {
          dispatch?.({
            type: 'store/addComponent',
            payload: component,
          });
        }
      };
      return (...);
    }

    在画布中拖动

    拖动主要依赖组件的初始位置,鼠标开始位置、结束位置。根据后两组得到鼠标移动的距离,和初始位置相加后,得到最终位置。

    function DragEditor(props: IEditorProps) {
      const { dvaStore, dispatch } = props;
      const [startAxis, setStartAxis] = React.useState({ x: 0, y: 0 }); // 鼠标开始拖动时的位置
      const handleDragStart = (e: React.DragEvent<HTMLDivElement>) => {
        setStartAxis({ x: e.clientX, y: e.clientY });
      };
      const handleDragEnd = (e: React.DragEvent<HTMLDivElement>, data: IComponentSchema) => {
        // 鼠标移动的距离
        const displacementX = e.clientX - startAxis.x;
        const displacementY = e.clientY - startAxis.y;
        // 计算组件的终点位置:初始位置 + 鼠标移动的距离
        const endX = Number(data.style.left) + displacementX;
        const endY = Number(data.style.top) + displacementY;
        // 限制坐标的最小值为 0
        const top = Math.max(endY, 0);
        const left = Math.max(endX, 0);
        // 更新当前组件样式
        dispatch?.({
          type: 'store/setShapeStyle',
          payload: { top, left },
        });
      };
      return (
          {dvaStore.componentsData.map((i) => {
            return (
              <RenderComponent
                type={i.component}
                componentData={i}
                key={i.generateId}
                onDragStart={handleDragStart}
                onDragEnd={(e) => handleDragEnd(e, i)}
              />
            );
          })}
      );
    }

    数据结构

    最后,就是组件和数据结构的设计,RenderComponent 是一个自定义的组件,会根据传入的 type 属性渲染对应的组件。组件的数据结构设计如下:

    export const COMPONENT_LIST = [
      {
        component: 'Rectangle', // 组件名称
        label: '矩形', // 左侧 Blocks 组件列表中显示的名字
        propValue: '', // 组件所使用的值
        icon: 'BorderOuterOutlined', // 左侧组件列表中显示的 icon 图标
        animations: [], // 动画列表
        events: {}, // 事件列表
        style: {    // 组件样式
          width: 100,
          height: 100,
          top: 0,
          left: 0,
        },
      },
      {
        component: 'Text',
        label: '文字',
        propValue: '文字',
        icon: '',
        animations: [],
        events: {},
        style: {
          width: 200,
          height: 33,
          fontSize: 14,
          fontWeight: 500,
          lineHeight: '',
          letterSpacing: 0,
          textAlign: '',
          color: '',
        },
      },
    ];

    到此,相信大家对“React怎么结合Drag API实现拖拽效果”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI