---
title: "SigmaOverlay"
description: "跟随画布定位的 DOM 覆盖层，可锚定到节点或图坐标，缩放平移时自动同步位置。"
canonical_url: "https://sigma.mhaibaraai.cn/docs/components/overlay"
---
# SigmaOverlay

> 跟随画布定位的 DOM 覆盖层，可锚定到节点或图坐标，缩放平移时自动同步位置。

## 用法

sigma 把图画在 canvas 上，canvas 里没有 DOM，也就放不了按钮、表单、富文本。`SigmaOverlay` 是一块普通的 `div`，库只负责让它的位置跟着画布同步，放什么完全由你决定——默认插槽没有作用域。

`SigmaTooltip`、`SigmaPopover`、`SigmaContextMenu` 都建在它之上，需要完全自定义的浮动内容时才直接用它。

> [!NOTE]
> 
> .sigma-overlay
> 
>  的 
> 
> pointer-events
> 
>  是 
> 
> none
> 
> ，鼠标事件会穿透到画布上。里面要放可点的东西，得在你自己的元素上恢复 
> 
> pointer-events: auto
> 
> 。

### `node`

锚定到节点的 key。节点的显示坐标已被 sigma 归一化，组件走 `framedGraphToViewport()` 换算，与 sigma 自身定位标签的方式一致。相机移动、图变更、容器缩放都会触发重绘，覆盖层跟着重绘同步位置。

```vue [OverlayNodeExample.vue]
<script setup lang="ts">
const props = withDefaults(defineProps<{ node?: string }>(), { node: '11.0' })

const { data } = await useFetch('/api/small.json')

const label = computed(() => data.value?.nodes.find(item => item.key === props.node)?.attributes.label)
</script>

<template>
  <SigmaGraph :data="data">
    <SigmaOverlay :node="node" :offset="[0, -24]">
      <UBadge color="neutral" variant="solid" class="-translate-x-1/2 -translate-y-full whitespace-nowrap">
        锚定在 {{ label }}
      </UBadge>
    </SigmaOverlay>
  </SigmaGraph>
</template>
```

> [!TIP]
> 
> 节点被 reducer 标记为 
> 
> hidden
> 
> 、或从图上移除时，覆盖层自动隐藏。
> 
> node
> 
>  与 
> 
> position
> 
>  二选一，两个都传时 
> 
> position
> 
>  被忽略。

### `position`

锚定到图坐标，走的是 `graphToViewport()`——**与 node 不是同一个换算函数**。`getNodeDisplayData()` 返回的是 framed 坐标，`position` 吃的是原始图坐标，自己写覆盖层时把两者混用，内容会整体错位。

下面把两个覆盖层指向同一个点，贴合说明换算正确：

```vue [OverlayPositionExample.vue]
<script setup lang="ts">
const { data } = await useFetch('/api/small.json')

const anchor = computed(() => data.value!.nodes.find(item => item.key === 'd')!.attributes)
</script>

<template>
  <SigmaGraph :data="data">
    <SigmaOverlay :position="{ x: anchor.x, y: anchor.y }">
      <UBadge size="sm" color="warning" class="-translate-x-full -translate-y-1/2">
        position
      </UBadge>
    </SigmaOverlay>

    <SigmaOverlay node="d">
      <UBadge size="sm" class="-translate-y-1/2">
        node
      </UBadge>
    </SigmaOverlay>
  </SigmaGraph>
</template>
```

### `offset`

相对锚点的像素偏移。组件把位置写成内联 `transform: translate(...)`，元素的**左上角**落在锚点上；居中、上移这类版式由内容自己的 CSS `translate` 决定，两者叠加：

```vue [OverlayOffsetExample.vue]
<script setup lang="ts">
const props = withDefaults(defineProps<{
  offsetX?: number | string
  offsetY?: number | string
}>(), {
  offsetX: 0,
  offsetY: -24
})

const offset = computed<[number, number]>(() => [Number(props.offsetX), Number(props.offsetY)])

const { data } = await useFetch('/api/small.json')
</script>

<template>
  <SigmaGraph :data="data">
    <SigmaOverlay node="c" :offset="offset">
      <UBadge color="warning" size="sm">
        左上角落在锚点上
      </UBadge>
    </SigmaOverlay>

    <SigmaOverlay node="c" :offset="offset">
      <UBadge color="info" size="sm" class="-translate-x-1/2 -translate-y-full">
        translate: -50% -100%
      </UBadge>
    </SigmaOverlay>
  </SigmaGraph>
</template>
```

> [!NOTE]
> 
> 内置的 
> 
> .sigma-tooltip
> 
>  与 
> 
> .sigma-popover
> 
>  都写了 
> 
> translate: -50% -100%
> 
>  做水平居中、底边贴合，
> 
> offset
> 
>  只负责把气泡从节点圆心推到圆周之外这类纯像素微调。

### `visible`

组件用 `v-show` 保留 DOM，只切 `display`。重新出现时不必重建，但也意味着**插槽内容始终挂载**——隐藏期间定时器、请求、事件监听照跑：

```vue [OverlayVisibleExample.vue]
<script setup lang="ts">
defineProps<{
  visible?: boolean
}>()

const Badge = defineComponent({
  props: { label: { type: String, required: true } },
  setup(props) {
    const since = new Date().toLocaleTimeString()
    return () => h(
      'div',
      { class: '-translate-x-1/2 -translate-y-full rounded-md bg-inverted px-2 py-1 text-xs text-inverted whitespace-nowrap' },
      `${props.label} · 挂载于 ${since}`
    )
  }
})

const { data } = await useFetch('/api/small.json')
</script>

<template>
  <SigmaGraph :data="data">
    <SigmaOverlay node="b" :offset="[0, -24]" :visible="visible">
      <Badge label="没有 v-if" />
    </SigmaOverlay>

    <SigmaOverlay node="c" :offset="[0, -24]" :visible="visible">
      <Badge v-if="visible" label="加了 v-if" />
    </SigmaOverlay>
  </SigmaGraph>
</template>
```

> [!WARNING]
> 
> 插槽内容必须自行 
> 
> v-if
> 
> ，否则隐藏只是视觉上的。三个派生组件都在插槽上加了 
> 
> v-if
> 
> ，直接用 
> 
> SigmaOverlay
> 
>  时要自己补。

## 示例

### 默认插槽

默认插槽没有作用域，内容是什么完全由使用方决定。要放可交互的卡片，两件事得自己做：插槽内容加 `v-if`，以及在自己的元素上恢复 `pointer-events: auto`。

```vue [OverlaySlotExample.vue]
<script setup lang="ts">
defineProps<{
  open?: boolean
}>()

const votes = shallowRef(0)

const { data } = await useFetch('/api/data.json')
</script>

<template>
  <SigmaGraph :data="data">
    <SigmaOverlay node="11.0" :offset="[0, -20]" :visible="open">
      <div
        v-if="open"
        class="pointer-events-auto w-50 -translate-x-1/2 -translate-y-full rounded-lg border border-accented bg-default p-3 text-xs shadow-lg"
      >
        <strong>Valjean</strong>
        <p class="mt-1.5 mb-2 text-muted">
          覆盖层不规定内容，表单、按钮、富文本都能放。
        </p>
        <div class="flex gap-1.5">
          <UButton size="xs" variant="soft" :label="`赞同 ${votes}`" @click="votes += 1" />
        </div>
      </div>
    </SigmaOverlay>
  </SigmaGraph>
</template>
```

> [!TIP]
> 
> 需要作用域（命中项、属性、关闭回调）时，用 
> 
> SigmaTooltip
> 
>  / 
> 
> SigmaPopover
> 
>  / 
> 
> SigmaContextMenu
> 
> ，不必从这里自己搭。

## API

### Props

```ts
/**
 * Props for the SigmaOverlay component
 */
interface SigmaOverlayProps {
  /**
   * 锚定到该节点，节点被隐藏或不存在时自动隐藏
   */
  node?: string | undefined;
  /**
   * 锚定到图坐标，与 `node` 二选一
   */
  position?: Coordinates | undefined;
  /**
   * 相对锚点的像素偏移 `[x, y]`
   * @default [0, 0]
   */
  offset?: [number, number] | undefined;
  /**
   * 是否显示
   * @default true
   */
  visible?: boolean | undefined;
}
```

### Slots

```ts
/**
 * Slots for the SigmaOverlay component
 */
interface SigmaOverlaySlots {
  default(): any;
}
```


## Sitemap

See the full [sitemap](https://sigma.mhaibaraai.cn/sitemap.md) for all pages.
