---
title: "useSigmaLayout"
description: "五种布局算法的统一入口，支持按连通分量分别布局，并托管 ForceAtlas2 与 Noverlap 的 worker 生命周期。"
canonical_url: "https://sigma.mhaibaraai.cn/docs/composables/use-sigma-layout"
---
# useSigmaLayout

> 五种布局算法的统一入口，支持按连通分量分别布局，并托管 ForceAtlas2 与 Noverlap 的 worker 生命周期。

## 用法

一次性布局跑完就结束，调 `assign()`；迭代型布局要持续跑，调 `start()` / `stop()`：

```vue [UseSigmaLayoutExample.vue]
<script setup lang="ts">
import type { SigmaLayoutName } from '@movk/sigma'

defineProps<{ name: SigmaLayoutName }>()

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

<template>
  <SigmaGraph :data="data" :settings="{ itemSizesReference: 'screen' }">
    <UseSigmaLayoutPanel :key="name" :name="name" />
  </SigmaGraph>
</template>
```

> [!WARNING]
> 
> 布局会大幅改写坐标跨度：
> 
> circular
> 
>  / 
> 
> random
> 
>  默认只铺开 1~2 个单位，ForceAtlas2 会收敛到几十个单位。而 v4 的 
> 
> size
> 
>  是
> 
> 图坐标单位
> 
> ，跨度一缩节点就成倍胀大，画面直接糊掉。示例都传了 
> 
> itemSizesReference: 'screen'
> 
>  让 
> 
> size
> 
>  退回像素语义。

### `name`

`'forceatlas2'` 与 `'noverlap'` 是迭代型，`isSupervised` 为 `true`，支持后台持续迭代、可以放进 worker；`'circular'`、`'circlepack'`、`'random'` 是一次性的，算一遍写回坐标就结束，`start()` 在这类布局上等价于 `assign()`。

`isSupervised` 是**普通布尔值，不是 ref**——布局名在调用时就定了，不会变。

> [!NOTE]
> 
> 全部布局包都是
> 
> 可选 peer
> 
> ，用到时才动态导入，未安装时抛出的错误里带有安装命令：
> 
> forceatlas2
> 
>  → 
> 
> graphology-layout-forceatlas2
> 
> ，
> 
> noverlap
> 
>  → 
> 
> graphology-layout-noverlap
> 
> ，其余三个 → 
> 
> graphology-layout
> 
> 。

### `worker`

只在「迭代型布局 + `worker: true`」时才真的开 worker，否则 `start()` 退化成一次性计算，而且**不会**把 `isRunning` 置为 `true`。`assign()` 永远是一次性的，**忽略这个选项**。

```vue [UseSigmaLayoutWorkerExample.vue]
<script setup lang="ts">
import type { StylesDeclaration } from 'sigma/types'

const { data } = await useFetch('/api/euroSIS.json', { server: false })

const styles: StylesDeclaration = {
  nodes: {
    color: '#3b82f6',
    size: { attribute: 'nansi-degree', min: 2, max: 12, minValue: 1, maxValue: 125 }
  },
  edges: { color: '#e2e8f0' }
}
</script>

<template>
  <SigmaGraph
    v-if="data"
    :data="data"
    :styles="styles"
    :settings="{ hideEdgesOnMove: true, itemSizesReference: 'screen', renderLabels: false }"
  >
    <UseSigmaLayoutWorkerPanel />
  </SigmaGraph>
</template>
```

```ts
const { start, stop, isRunning } = useSigmaLayout('forceatlas2', {
  settings: { gravity: 1, scalingRatio: 10 }
})

await start()
setTimeout(stop, 3000)
```

worker 会持续占用线程，库在作用域销毁时自动 `kill()`，不必自己接 `onUnmounted`。`stop()` 只暂停迭代，worker 还在，`start()` 可以再启动；`kill()` 才是真的销毁。

> [!WARNING]
> 
> supervisor 绑定的是**首次调用 start() 时**的图实例。图实例被整个替换后（不是内容变化，是换了个 `Graph` 对象），必须先 `kill()` 再重新 `start()`，否则布局还在给旧图算坐标。
> 
> 迭代型布局在跑时会持续回写坐标，[`useSigmaDrag()`](https://sigma.mhaibaraai.cn/docs/composables/use-sigma-drag) 的拖拽结果会被立刻覆盖，需要手动摆位时先 `stop()`。

### `settings`

直接交给底层算法，不做键过滤。ForceAtlas2 两条路径都以 `inferSettings()` 的推断结果为基座，你传的键覆盖在上面：

```ts
useSigmaLayout('forceatlas2', {
  settings: { gravity: 1 } // 其余仍用 inferSettings 推断出的值
})
```

具体的键见 [graphology 布局文档](https://graphology.github.io/standard-library/layout-forceatlas2.html)。

### `iterations`

非 worker 模式下 forceatlas2 的迭代次数，默认 `100`。

### `byComponent`

ForceAtlas2 在互不相连的分量之间**只有斥力没有引力**，全图跑一遍会把分量推到四角、中间大片留白，节点一多画面就是一层均匀散开的尘埃。`byComponent` 让每个弱连通分量单独计算，分量之间的相对位置改由圆形打包决定：

```vue [UseSigmaLayoutComponentExample.vue]
<script setup lang="ts">
import type { SerializedGraph } from 'graphology-types'

const PALETTE = ['#f43f5e', '#3b82f6', '#22c55e', '#a855f7', '#f59e0b', '#14b8a6']

const data: SerializedGraph = {
  attributes: {},
  options: { type: 'undirected', multi: false, allowSelfLoops: false },
  nodes: PALETTE.flatMap((color, group) =>
    Array.from({ length: 3 + (group % 3) }, (_, index) => {
      const angle = (group * 5 + index) * 1.1
      return {
        key: `g${group}-${index}`,
        attributes: { x: Math.cos(angle) * 120, y: Math.sin(angle) * 120, size: 8, color }
      }
    })
  ),
  edges: PALETTE.flatMap((_, group) =>
    Array.from({ length: 2 + (group % 3) }, (_, index) => ({
      source: `g${group}-0`,
      target: `g${group}-${index + 1}`,
      attributes: {}
    }))
  )
}
</script>

<template>
  <SigmaGraph :data="data" :settings="{ itemSizesReference: 'screen' }">
    <UseSigmaLayoutComponentPanel />
  </SigmaGraph>
</template>
```

```ts
const { assign } = useSigmaLayout('forceatlas2', {
  worker: false,
  byComponent: true,
  settings: { gravity: 1, scalingRatio: 20, adjustSizes: true }
})
```

打包前每个分量先按节点数缩放到同样的密度，不按平均边长归一——星形分量的平均边长会被少数长边拉高，一圈叶子因此被压在同一个半径上。分量顺序与打包所用的随机源都是固定的，同一份数据每次结果一致。

> [!WARNING]
> 
> 单纯调 
> 
> gravity
> 
>  / 
> 
> scalingRatio
> 
>  解决不了这件事。
> 
>  相机 fit 会把整体尺度归一化，两个参数同时缩放布局的绝对尺寸，fit 之后看不出任何区别。唯一能改变观感的是让布局本身产生疏密结构。

> [!NOTE]
> 
> byComponent
> 
>  与 
> 
> worker
> 
>  互斥：worker 版每帧回写全图坐标，打包结果会被立刻覆盖。因此开启后 
> 
> isSupervised
> 
>  恒为 
> 
> false
> 
> ，
> 
> start()
> 
>  退化为 
> 
> assign()
> 
> 。打包用的 
> 
> circlepack
> 
>  来自可选依赖 
> 
> graphology-layout
> 
> 。

## 示例

### assign 与 start 怎么选

`assign()` 适合「摆一次位就不动了」的场景，比如加载完数据先跑一遍 ForceAtlas2 定型：

```ts
const { start, isRunning } = useSigmaLayout('circular')
await start() // 一次性布局上跑了一遍就结束，isRunning 始终是 false
```

## API

### useSigmaLayout()

`useSigmaLayout(name: SigmaLayoutName, options?: UseSigmaLayoutOptions): UseSigmaLayoutReturn`

#### Parameters

**name** (`SigmaLayoutName`) *required*: 布局算法名，取值为 'forceatlas2'、'noverlap'、'circular'、'circlepack'、'random'。

#### Options

**worker** (`boolean`): 默认 true —— 迭代型布局是否放到 worker 里跑，避免阻塞主线程。assign() 不受此项影响。

**settings** (`Record<string, unknown>`): 透传给底层算法的设置，不做键过滤。ForceAtlas2 下覆盖在 inferSettings() 的推断值之上。

**iterations** (`number`): 默认 100 —— 非 worker 模式下 forceatlas2 的迭代次数。

**byComponent** (`boolean | SigmaLayoutComponentOptions`): 默认 false —— 按弱连通分量分别布局，再把各分量的外接圆打包到一起。与 worker 互斥，需要可选依赖 graphology-layout。传对象可调打包参数：gap 默认 60，是分量之间的间距（图坐标）；spacing 默认 100，是归一化后每个节点分到的平均间距。

#### Returns

**isSupervised** (`boolean`): 该布局是否支持后台持续迭代。普通布尔值，不是 ref；byComponent 开启时恒为 false。

**isRunning** (`Readonly<Ref<boolean>>`): worker 是否正在运行。一次性布局上恒为 false。

**assign()** (`() => Promise<void>`): 计算一次并写回节点坐标，然后触发重绘。始终是一次性的，忽略 worker。

**start()** (`() => Promise<void>`): 启动持续迭代。非迭代型布局或 worker: false 时退化为 assign() 的行为。

**stop()** (`() => void`): 停止迭代。worker 仍存活，可再次 start()。

**kill()** (`() => void`): 终止并释放 worker。作用域销毁时自动调用。

类型从根出口取：

```ts
import type {
  SigmaLayoutComponentOptions,
  SigmaLayoutName,
  UseSigmaLayoutOptions,
  UseSigmaLayoutReturn
} from '@movk/sigma'
```


## Sitemap

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