Appearance
句柄简介
🌐 Introduction to Handles
把手是通常放置在节点边界上的小圆圈。它们用于将节点连接在一起,通过从一个把手拖动连接线到另一个把手,从而在节点之间形成连接(Edge)。
🌐 Handles are the small circles that are usually placed on the borders of a node. They are used to connect nodes together by dragging a connection-line from one handle to another, resulting in a connection (Edge) between the nodes.
句柄是 VueFlow 的重要组成部分,因为它们是用户在节点之间创建边的主要交互点。
🌐 Handles are a crucial part of VueFlow, as they are the main interaction point for a user to create edges between nodes.
没有句柄,基本上无法在节点之间创建边,因为句柄用于计算边的起点和终点。
🌐 Without handles, it is basically impossible to create edges between nodes, as the handles are used to calculate the source and target points for edges.
<Handle> 组件
🌐 <Handle> Component
<Handle> 组件是由 @vue-flow/core 导出的一个组件,你可以使用它为节点创建一个句柄。它是 <div> 元素的一个封装器,提供了启动连接所需的事件处理程序绑定。
🌐 The <Handle> component is a component exported by @vue-flow/core that you can use to create a handle for a node. It is a wrapper around a <div> element that provides the necessary event handler bindings to start connections.
<Handle> 组件可以在(自定义)节点组件中使用,以为节点创建连接点。 在节点组件上下文之外使用 <Handle> 组件不会按预期工作,因此尽量避免这样做。
🌐 The <Handle> component can be used in a (custom) node component to create handles for the node. Using <Handle> components outside a node component context will not work as expected, so try to avoid this.
vue
<script setup>
import { Handle } from '@vue-flow/core'
defineProps(['id', 'sourcePosition', 'targetPosition', 'data'])
</script>
<template>
<Handle type="source" :position="sourcePosition" />
<span>{{ data.label }}</span>
<Handle type="target" :position="targetPosition" />
</template>句柄位置
🌐 Handle Positions
句柄可以放置在以下位置:
🌐 Handles can be placed on the following positions:
TopRightBottomLeft
每个位置对应节点的一侧。句柄的位置还决定了从句柄绘制边时边会向哪个方向弯曲。
🌐 Each position corresponds to a side of the node. The position of the handle also determines which direction an edge will bend towards when drawn from and to a handle.
例如,带有 position="Position.Top" 的句柄在从该句柄绘制时会导致边缘向上弯曲。带有 position="Position.Right" 的句柄在绘制到该句柄时会导致边缘向左弯曲。
🌐 For example a handle with position="Position.Top" will result in an edge that bends to the top when drawn from that handle. A handle with position="Position.Right" will result in an edge that bends to the left when drawn to that handle.
调整句柄位置
🌐 Adjusting Handle Positions
句柄使用 CSS 的 absolute 位置定位在各自的一侧。这意味着你可以通过将句柄封装在具有 relative 位置的容器中,来调整句柄与哪个元素对齐。
🌐 Handles are positioned on their respective side using CSS with an absolute position. That means you can adjust what element a handle aligns itself with by wrapping it in a container that has a relative position.
vue
<template>
<div>
<span>{{ data.label }}</span>
<div style="position: relative; padding: 10px">
<Handle type="source" :position="Position.Right" />
<Handle type="target" :position="Position.Left" />
</div>
</div>
</template>多个句柄
🌐 Multiple Handles
一个节点可以有多个控制点,控制点的数量不受限制,你可以根据需要使用任意数量的控制点。 当使用相同类型的多个控制点(source 或 target)时,每个控制点都需要有一个唯一的 ID。
🌐 A node can have multiple handles, the number of handles is not limited and you can use as many handles as you need. When using multiple handles of the same type (source or target), each handle needs to have a unique id.
vue
<!-- each of these handles needs a unique id since we're using two `source` type handles -->
<Handle id="source-a" type="source" :position="Position.Right" />
<Handle id="source-b" type="source" :position="Position.Right" />
<!-- each of these handles needs a unique id since we're using two `target` type handles -->
<Handle id="target-a" type="target" :position="Position.Left" />
<Handle id="target-b" type="target" :position="Position.Left" />id 属性用于在创建节点之间的边时标识句柄。如果未提供 id,将使用所需类型的第一个句柄。
🌐 The id prop is used to identify the handle when creating edges between nodes. If no id is provided, the first handle of the necessary type will be used.
ts
const { onConnect } = useVueFlow()
onConnect(({ source, target, sourceHandle, targetHandle }) => {
console.log('source', source)
console.log('target', target)
// these are the handle ids of the source and target node
// if no id is specified these will be `null`, meaning the first handle of the necessary type will be used
console.log('sourceHandle', sourceHandle)
console.log('targetHandle', targetHandle)
})使用多个句柄定位
🌐 Positioning with Multiple Handles
有时你可能想在同一侧添加多个控制点。在这种情况下,你往往会发现两个控制点叠在一起,而不是并排放置。控制点不会自动布局,因此你需要手动调整它们的位置。
🌐 Sometimes you want to add multiple handles to the same side. In that case you often end up having two handles on top of each other instead of next to each other. Handles will not layout themselves automatically, so you need to manually adjust their position.
你可以使用 CSS 样式来实现。例如,你可以设置 top 和 bottom 属性,将句柄定位在节点右侧的顶部和底部。
🌐 You can do so using CSS styles. For example, you can set the top and bottom properties to position the handles on the top and bottom of the right side of the node.
vue
<Handle id="source-a" type="source" :position="Position.Right" style="top: 10px" />
<Handle id="source-b" type="source" :position="Position.Right" style="bottom: 10px; top: auto;" />隐藏句柄
🌐 Hidden Handles
在某些情况下,你可能根本不想显示句柄。你可以通过将 opacity: 0 设置为该句柄的样式来隐藏句柄。
🌐 In some cases you might not want to display a handle at all. You can hide a handle by setting opacity: 0 as the styles for that handle.
你无法通过将句柄从 DOM 中移除(例如使用 v-if 或 v-show)来隐藏它,因为那样会破坏边缘的计算。
🌐 You cannot hide a handle by removing it from the DOM (for example using v-if or v-show) as that would break the calculations for the edges.
vue
<Handle type="source" :position="Position.Right" style="opacity: 0" />限制连接
🌐 Limiting Connections
你可以通过在 <Handle> 组件上设置 connectable 属性来限制一个句柄可以拥有的连接数量。
🌐 You can limit the number of connections a handle can have by setting the connectable prop on the <Handle> component.
此属性接受布尔值(默认为 true)、数字(最大连接数),或返回布尔值的函数。
🌐 This prop accepts a boolean value (defaults to true), a number (the maximum number of connections), or a function that returns a boolean.
使用函数可以实现自定义逻辑来确定是否可以连接句柄。
🌐 Using a function allows you to implement custom logic to determine if a handle can be connected to or not.
vue
<script lang="ts" setup>
import { Position, Handle, type HandleConnectableFunc } from '@vue-flow/core'
const handleConnectable: HandleConnectableFunc = (node, connectedEdges) => {
// only allow connections if the node has less than 3 connections
return connectedEdges.length < 3
}
</script>
<template>
<Handle type="source" :position="Position.Right" :connectable="handleConnectable" />
</template>连接模式
🌐 Connection Mode
默认情况下,Vue Flow 将使用 <VueFlow :connection-mode="ConnectionMode.Loose" />,这允许你将边连接到任何句柄。这意味着允许在 source 和另一种类型的 source <Handle> 之间进行连接。
🌐 By default, Vue Flow will use <VueFlow :connection-mode="ConnectionMode.Loose" /> which allows you to connect edges to any handle. That means connections between a source and another source type <Handle> are allowed.
如果你想限制连接仅在 source 和 target 类型的句柄之间建立,你可以将 connection-mode 属性设置为 ConnectionMode.Strict。
🌐 If you want to restrict connections to only be made between source and target type handles, you can set the connection-mode prop to ConnectionMode.Strict.
vue
<script setup>
import { ConnectionMode, VueFlow } from '@vue-flow/core'
</script>
<template>
<VueFlow :connection-mode="ConnectionMode.Strict" />
</template>动态句柄位置和动态添加/移除句柄
🌐 Dynamic Handle Positions & Adding/Removing Handles Dynamically
TIP
在 Vue Flow 1.x 中,动态添加节点连接点时无需手动调用 updateNodeInternals。组件挂载后,连接点会自动尝试附加到节点上。不过,如果由于某些原因未能如预期发生,你可以遵循以下指南来强制 Vue Flow 更新节点内部信息。
有时,你可能需要动态修改句柄的位置或以编程方式向节点添加新的句柄。在这种情况下,Vue Flow API 中的 updateNodeInternals 方法非常有用。
🌐 At times, you may need to modify handle positions dynamically or programmatically add new handles to a node. In this scenario, the updateNodeInternals method found in Vue Flow's API comes in handy.
在处理动态句柄时,调用此方法至关重要。如果不调用,节点可能无法识别这些新句柄,导致边缘错位。
🌐 Invoking this method is vital when dealing with dynamic handles. If not, the node might fail to recognize these new handles, resulting in misaligned edges.
updateNodeInternals 函数可以通过以下两种方式之一进行部署:
🌐 The updateNodeInternals function can be deployed in one of two ways:
- 使用 store 操作: 这种方法允许你通过将节点 ID 传入该方法,一次性更新多个节点。
- 从你的自定义节点组件中触发
updateNodeInternals事件: 这不需要传递任何参数。
js
import { useVueFlow } from '@vue-flow/core'
const { updateNodeInternals } = useVueFlow()
const onSomeEvent = () => {
updateNodeInternals(['1'])
}vue
<script setup>
const emits = defineEmits(['updateNodeInternals'])
const onSomeEvent = () => {
emits('updateNodeInternals')
}
</script>