Skip to Content
示例Interaction

拖放

在这些示例中,我们使用自定义 Sidebar 组件在从侧边栏拖动到 React Flow 面板时创建新节点。

🌐 In these examples, we use a custom Sidebar component to create new nodes when dragging from the sidebar into the React Flow pane.

基于节点的工作流编辑器中,拖放用户界面非常常见。在 React Flow 面板之外的拖放行为不是内置的,但可以使用原生的 HTML 拖放 API 指针事件  或第三方库如 react-draggable  来实现。

🌐 A drag and drop user interface is very common for node-based workflow editors. The drag and drop behavior outside of the React Flow pane is not built in but can be implemented with the native HTML Drag and Drop API , Pointer Events , or a third party library like react-draggable .

使用 HTML 拖放 API 进行拖放

🌐 Drag and Drop with HTML Drag and Drop API

HTML 拖放(Drag and Drop)API 是一个原生 API,所有主要浏览器都支持它。它是一个简单的 API,允许你在页面上的不同元素之间拖放元素。

🌐 The HTML Drag and Drop API is a native API that is supported by all major browsers. It is a simple API that allows you to drag and drop elements between different elements on the page.

注意: HTML 拖放 API 在触摸设备上不完全支持。 请参阅下面的示例,了解使用指针事件的替代方法。

import React, { useRef, useCallback } from 'react'; import { ReactFlow, ReactFlowProvider, addEdge, useNodesState, useEdgesState, Controls, useReactFlow, Background, } from '@xyflow/react'; import '@xyflow/react/dist/style.css'; import Sidebar from './Sidebar'; import { DnDProvider, useDnD } from './DnDContext'; const initialNodes = [ { id: '1', type: 'input', data: { label: 'input node' }, position: { x: 250, y: 5 }, }, ]; let id = 0; const getId = () => `dndnode_${id++}`; const DnDFlow = () => { const reactFlowWrapper = useRef(null); const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [edges, setEdges, onEdgesChange] = useEdgesState([]); const { screenToFlowPosition } = useReactFlow(); const [type] = useDnD(); const onConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)), []); const onDragOver = useCallback((event) => { event.preventDefault(); event.dataTransfer.dropEffect = 'move'; }, []); const onDrop = useCallback( (event) => { event.preventDefault(); // check if the dropped element is valid if (!type) { return; } // project was renamed to screenToFlowPosition // and you don't need to subtract the reactFlowBounds.left/top anymore // details: https://reactflow.dev/whats-new/2023-11-10 const position = screenToFlowPosition({ x: event.clientX, y: event.clientY, }); const newNode = { id: getId(), type, position, data: { label: `${type} node` }, }; setNodes((nds) => nds.concat(newNode)); }, [screenToFlowPosition, type], ); const onDragStart = (event, nodeType) => { setType(nodeType); event.dataTransfer.setData('text/plain', nodeType); event.dataTransfer.effectAllowed = 'move'; }; return ( <div className="dndflow"> <div className="reactflow-wrapper" ref={reactFlowWrapper}> <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect} onDrop={onDrop} onDragStart={onDragStart} onDragOver={onDragOver} fitView > <Controls /> <Background /> </ReactFlow> </div> <Sidebar /> </div> ); }; export default () => ( <ReactFlowProvider> <DnDProvider> <DnDFlow /> </DnDProvider> </ReactFlowProvider> );

使用指针事件进行拖放

🌐 Drag and Drop with Pointer Events

实现拖放的另一种方式是使用 指针事件

🌐 Another way to implement drag and drop is to use pointer events.

Pointer Events API 是一个现代的 API,所有主流浏览器都支持它。它的实现比 HTML 拖放(Drag and Drop)API 稍微复杂一些,但通过使用指针事件,我们可以确保拖放行为在所有设备上(包括鼠标和触摸)保持一致。

🌐 The Pointer Events API is a modern API that is supported by all major browsers. It is slightly more complex to implement than the HTML Drag and Drop API, but by using pointer events we can make sure that the drag and drop behavior is consistent across all devices, both mouse and touch.

import { useCallback } from 'react'; import { Background, Connection, Controls, ReactFlow, ReactFlowProvider, addEdge, useEdgesState, useNodesState, } from '@xyflow/react'; import '@xyflow/react/dist/style.css'; import { Sidebar } from './Sidebar'; import { DnDProvider } from './useDnD'; const initialNodes = [ { id: '1', type: 'input', data: { label: 'input node' }, position: { x: 250, y: 5 }, }, ]; function DnDFlow() { const [nodes, _, onNodesChange] = useNodesState(initialNodes); const [edges, setEdges, onEdgesChange] = useEdgesState([]); const onConnect = useCallback( (params: Connection) => setEdges((eds) => addEdge(params, eds)), [], ); return ( <div className="dndflow"> <div className="reactflow-wrapper"> <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect} fitView > <Controls /> <Background /> </ReactFlow> </div> <Sidebar /> </div> ); } export default () => ( <ReactFlowProvider> <DnDProvider> <DnDFlow /> </DnDProvider> </ReactFlowProvider> );

使用 Neodrag 进行拖放

🌐 Drag and Drop with Neodrag

Neodrag  是一个提供简单易用的拖放 API 的库。在实际应用中,你可能希望使用 Neodrag 库来以跨平台兼容的方式处理拖放行为,同时兼容鼠标和触控设备。

import { useCallback } from 'react'; import { Background, Connection, Controls, ReactFlow, ReactFlowProvider, addEdge, useEdgesState, useNodesState, } from '@xyflow/react'; import '@xyflow/react/dist/style.css'; import { Sidebar } from './Sidebar'; const initialNodes = [ { id: '1', type: 'input', data: { label: 'input node' }, position: { x: 250, y: 5 }, }, ]; function DnDFlow() { const [nodes, _, onNodesChange] = useNodesState(initialNodes); const [edges, setEdges, onEdgesChange] = useEdgesState([]); const onConnect = useCallback( (params: Connection) => setEdges((eds) => addEdge(params, eds)), [], ); return ( <div className="dndflow"> <div className="reactflow-wrapper"> <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect} fitView > <Controls /> <Background /> </ReactFlow> </div> <Sidebar /> </div> ); } export default () => ( <ReactFlowProvider> <DnDFlow /> </ReactFlowProvider> );
Last updated on