---
title: "useSigmaCamera"
description: "相机操作。缩放、复位、聚焦节点、避开浮层遮挡地容纳节点，以及图坐标到屏幕坐标的换算。"
canonical_url: "https://sigma.mhaibaraai.cn/docs/composables/use-sigma-camera"
---
# useSigmaCamera

> 相机操作。缩放、复位、聚焦节点、避开浮层遮挡地容纳节点，以及图坐标到屏幕坐标的换算。

## 用法

全部基于原生 `sigma.getCamera()`，需要更底层的控制随时可以直接拿实例自己调。所有动画方法返回 `Promise`，内部先 `await whenReady()`，实例就绪前调用不会丢失：

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

<template>
  <SigmaGraph :data="data">
    <UseSigmaCameraPanel />
  </SigmaGraph>
</template>
```

```vue [UseSigmaCameraPanel.vue]
<script setup lang="ts">
const { zoomIn, zoomOut, reset, gotoNode, fitTo } = useSigmaCamera()
</script>

<template>
  <button type="button" @click="gotoNode('11.0', { ratio: 0.4 })">
    聚焦 Valjean
  </button>
</template>
```

> [!WARNING]
> 
> fitTo()
> 
>  依赖可选 peer 
> 
> @sigma/utils
> 
> 。未安装时抛出的错误里带有安装命令，捕获后直接展示给用户即可。

## 示例

### 浮层遮挡下的 fit

`fitTo()` 默认只认整块舞台。真实应用的侧栏、工具栏、详情面板往往**浮在画布之上**，不挤压舞台尺寸，于是 fit 出来的内容有一部分正好压在浮层底下。`insets` 给出四周的遮挡像素宽度，fit 结果就落在扣掉遮挡后的可用区里：

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

<template>
  <SigmaGraph :data="data">
    <div class="absolute inset-y-0 left-0 z-10 flex w-60 items-center justify-center border-r border-default bg-default/90 text-muted text-xs">
      侧栏 240px
    </div>

    <UseSigmaCameraInsetsPanel />
  </SigmaGraph>
</template>
```

```ts
await fitTo(nodes, {
  insets: { left: 240, top: 96 },
  minRatio: 0.12
})
```

实现是先取上游算出的基准相机态，再按可用区相对整屏收缩的比例退远，并把编组中心挪到可用区中心。省略 `insets` 与 `minRatio` 时走的仍是 `@sigma/utils` 的原实现，行为一字不差。

`minRatio` 挡的是「一跳怼到脸上」：紧凑邻域若任由 fit 收敛，比例会小到十几倍放大。不要改用 sigma 的 `minCameraRatio` 替代——那会让居中偏移按未钳制的比例算，还会连手动滚轮放大一起挡住。

> [!WARNING]
> 
> 遮挡之和超过画布尺寸时（窗口收窄到浮层挤没可用区），库会按两侧原有比例收缩并保底一段可绘制宽度。不做这层保护的话退远倍数是 
> 
> Infinity
> 
> ，相机拿到 NaN 后整张图直接消失。

### 两套坐标系

`toViewport()` 换算的是**原始图坐标**，节点位置不要走这里，混用会让覆盖层整体错位：

```ts
// 节点位置——getNodeDisplayData 返回的是 sigma 归一化后的 framed 坐标
const screen = sigma.framedGraphToViewport(sigma.getNodeDisplayData(key))

// 原始图坐标
const screen = toViewport({ x: 0, y: 0 })
```

两个坐标系之间也可以直接互转，不必绕道屏幕坐标：

```ts
const { toFramedGraph, fromFramedGraph } = useSigmaCamera()

const framed = toFramedGraph({ x: 0, y: 0 }) // 业务坐标拿去和节点显示坐标比距离
const raw = fromFramedGraph(sigma.getNodeDisplayData(key)) // 拖拽后的位置写回业务数据
```

`gotoNode()` 内部把显示坐标直接交给相机而不做换算，是因为相机的 `x` / `y` 恰好也在 framed 坐标系。

## API

### useSigmaCamera()

`useSigmaCamera(): UseSigmaCameraReturn`

#### Returns

**zoomIn()** (`(options?: Partial<AnimateOptions> & { factor?: number }) => Promise<void>`): 放大，factor 为缩放倍数。

**zoomOut()** (`(options?: Partial<AnimateOptions> & { factor?: number }) => Promise<void>`): 缩小，factor 为缩放倍数。

**reset()** (`(options?: Partial<AnimateOptions>) => Promise<void>`): 复位到初始视角。

**goto()** (`(state: Partial<CameraState>, options?: Partial<AnimateOptions>) => Promise<void>`): 移动到指定相机状态。

**gotoNode()** (`(key: string, options?: Partial<AnimateOptions> & { ratio?: number }) => Promise<void>`): 移动到指定节点。节点不存在或尚未渲染时不动。

**fitTo()** (`(nodes?: string[], options?: SigmaFitOptions) => Promise<void>`): 调整视口容纳指定节点，省略则容纳全图。节点集合为空时不动相机。需要可选 peer @sigma/utils。默认 true —— 是否动画过渡。画布四周被浮层遮挡的像素宽度（top / right / bottom / left），fit 结果会落在扣掉遮挡后的可用区里。省略时行为与上游的 fitViewportToNodes 完全一致。相机比例下限，挡住紧凑邻域「一跳怼到脸上」。钳制发生在算居中偏移之前。

**getState()** (`() => CameraState | null`): 当前相机状态，未就绪时为 null。

**toViewport()** (`(point: Coordinates) => Coordinates | null`): 原始图坐标转屏幕坐标，未就绪时为 null。节点位置请改用 sigma.framedGraphToViewport()。

**toFramedGraph()** (`(point: Coordinates) => Coordinates | null`): 原始图坐标转 framed 归一化坐标，未就绪时为 null。

**fromFramedGraph()** (`(point: Coordinates) => Coordinates | null`): framed 归一化坐标转原始图坐标，未就绪时为 null。getNodeDisplayData() 给的就是 framed 坐标，把拖拽后的位置写回业务数据要经过这里。

`AnimateOptions` 来自 `sigma/utils`，`CameraState` 与 `Coordinates` 来自 `sigma/types`。本库自己的类型从根出口取：

```ts
import type { SigmaFitInsets, SigmaFitOptions, UseSigmaCameraReturn } from '@movk/sigma'
```


## Sitemap

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