# 自定义 UI 控件 (/zh-CN/realtime-media/whiteboard/fastboard-sdk/build/manage-whiteboard/customize-widget)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

<_PlatformTabsGroup>
<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="android" />
Fastboard SDK 提供了丰富的内置 UI 控件，包括工具条控件、撤回/重做控件、缩放控件和翻页控件。这些控件支持一些轻量化的配置，例如隐藏/展示控件等。如果你的业务场景需要用到更多功能，你可以自行实现控件并添加到白板中。

<DocsImage alt="Fastboard 内置 UI 控件示意图" src="https://web-cdn.agora.io/docs-portal/img/whiteboard/ui-widgets.png" displayWidth="55%" />

本文介绍如何使用 Fastboard SDK 修改白板内置 UI 控件的配置，以及如何将自定义的 UI 控件添加到白板中。

## 前提条件 [#前提条件]

开始前，请确保你已在项目中集成声网 Fastboard SDK 并加入房间，详见[加入白板房间](/zh-CN/realtime-media/whiteboard/fastboard-sdk/get-started/join-room-fb)。

## 实现方法 [#实现方法]

### 修改内置 UI 控件配置 [#修改内置-ui-控件配置]

你可以在创建 Fastboard UI 对象后，通过修改 [`FastboardUIConfig`](/zh-CN/api-reference/whiteboard/fastboard/web#fastboarduiconfig) 对白板内置 UI 控件进行一些简单的配置，例如隐藏/展示控件。参考以下代码：

```javascript tab="JavaScript" tabGroup="language"
// 创建 ui 实例
const ui = createUI(fastboard, container);
// 更新工具条配置
ui.update({ config: {  
  toolbar: {
    enable: true,
    placement: 'left',
    items: ["clicker", "selector", "pencil", "text", "shapes", "eraser", "clear"],
    apps: { enable: true },
  },
  redo_undo: { enable: true },
  zoom_control: { enable: true },
  page_control: { enable: true }, } });

// 隐藏缩放控件
ui.update({ config: { zoom_control: { enable: false } } });

// 设置工具条仅展示铅笔和橡皮工具
const toolbar_items = ["pencil", "eraser"];
ui.update({ config: { toolbar: { items: toolbar_items } } });
```

```jsx tab="React" tabGroup="language"
// 控件默认配置
<Fastboard app={fastboard} config={{
  toolbar: {
    enable: true,
    placement: 'left',
    items: ["clicker", "selector", "pencil", "text", "shapes", "eraser", "clear"],
    apps: { enable: true },
  },
  redo_undo: { enable: true },
  zoom_control: { enable: true },
  page_control: { enable: true },
}} />;

// 隐藏缩放控件
<Fastboard app={fastboard} config={{ zoom_control: { enable: false } }} />;

// 设置工具条仅展示铅笔和橡皮工具
const toolbar_items = ["pencil", "eraser"];
<Fastboard app={fastboard} config={{ toolbar: { items: toolbar_items } }} />;
```

### 使用自定义 UI 控件 [#使用自定义-ui-控件]

以定制工具条控件为例，你可以参考以下步骤将你自行实现的工具条控件添加到白板中。

#### 禁用内置工具条 [#禁用内置工具条]

通过修改 [`FastboardUIConfig`](/zh-CN/api-reference/whiteboard/fastboard/web#fastboarduiconfig) 中的 `toolbar` 配置项，禁用 Fastboard 内置的工具条：

```javascript tab="JavaScript" tabGroup="language"
const ui = createUI(fastboard, container);
ui.update({ config: {
  // highlight-start
  toolbar: { enable: false },
  // highlight-end
  redo_undo: { enable: true },
  zoom_control: { enable: true },
  page_control: { enable: true },
  } });
```

```jsx tab="React" tabGroup="language"
<Fastboard app={fastboard} config={{
  // highlight-start
  toolbar: { enable: false },
  // highlight-end
  redo_undo: { enable: true },
  zoom_control: { enable: true },
  page_control: { enable: true },
}} />;
```

#### 添加自定义 UI 控件 [#添加自定义-ui-控件]

参考以下步骤将自定义 UI 控件添加至白板：

1. 自行开发自定义 UI 控件。
2. 将控件和 Fastboard 组件放置于同一个 HTML 容器中。
3. 向自定义 UI 控件传入 Fastboard 实例对象以调用控件实现的逻辑。

:::info[信息]
- 你可以在自定义 UI 控件的代码中调用 `FastboardApp` 类下的 API 来实现工具切换、缩放、翻页等功能。调用这些 API 需要房间具有读写权限，详见[开发注意事项](#开发注意事项)。
- 我们提供了 Fastboard 内置的四种 UI 控件的实现代码供你参考，详见[内置控件源码](#内置控件源码)。
:::

以下示例创建了一个自定义工具条控件，并将其关联到白板中：

```javascript tab="JavaScript" tabGroup="language"
import { createFastboard, createUI } from "@netless/fastboard";

async function main() {
  const fastboard = await createFastboard(...);
  const container = createContainer();
  const ui = createUI(fastboard, container);
  ui.update({ config: { ...ui_config } });
  fastboard.destroy();
}
function createContainer() {
  const container = document.createElement("div");
  Object.assign(container.style, {
    height: "400px",
    border: "1px solid",
    background: "#f1f2f3",
  });
  // highlight-start
  document.body.appendChild(container);
  container.appendChild(createCustomToolbar());
  return container;
}
function createCustomToolbar() {
  const customToolbar = document.createEgetlement("div");
  // 自定义的工具条逻辑
  // ·····
  return customToolbar;
}
// highlight-end
```

```jsx tab="React" tabGroup="language"
import { useFastboard, Fastboard } from "@netless/fastboard-react";
import React from "react";
import { createRoot } from "react-dom/client";

function App() {
  const fastboard = useFastboard(...);
  return (
    <div
      style={{
        height: "400px",
        border: "1px solid",
        background: "#f1f2f3",
      }}
    >
      // highlight-start
      <Fastboard app={fastboard} />
      
      <CustomToolbar  app={fastboard} />
      // highlight-end
    </div>
  );
}

createRoot(document.getElementById("app")).render(<App />);
```

## 参考信息 [#参考信息]

### 开发注意事项 [#开发注意事项]

自定义 UI 控件逻辑中，调用 `FastboardApp` 类下的 API 时，例如 `setAppliance`，需要注意在读写权限下调用。你可以通过监听 `FastboardApp` 实例对象的 `writable` 属性获取当前是否具备读写权限。

```javascript
let app = await createFastboard({ ...config });
app.writable; // { value: true, subscribe/reaction, set? }
```

- `value`：直接取到当前值。
  - `true`：互动模式，即对白板具有读写权限。
  - `false`：订阅模式，即对白板具有只读权限。
- `subscribe`/`reaction`：设置一个监听器，监听权限值的变化。方法中的回调函数会在每次值发生变化时被调用，输出新的值，并返回一个停止监听的函数。其中，调用 `subscribe` 方法会立即执行一次，输出当前权限值，`reaction` 方法则不会。
- `set`：修改权限值。注意，修改不一定会同步更新到 `value` 上，当前权限值以 `value` 的值为准。

下面的示例代码中展示了两种监听方法的区别，并通过调用 `dispose` 取消 `subscribe` 注册的监听器：

```javascript
let dispose = app.writable.subscribe(value => {
  console.log("writable1:", value);
}); // 控制台输出 writable1: true
app.writable.reaction(value => {
  console.log("writable2:", value);
}); // 控制台无输出

app.writable.set(false); // 控制台输出 writable1: false, writable2: false

// 取消 subscribe 注册的监听器
dispose();

app.writable.set(true); // 控制台输出 writable2: true
```

### API 参考 [#api-参考]

- [`createUI`](/zh-CN/api-reference/whiteboard/fastboard/web#createui)
- [`UI`](/zh-CN/api-reference/whiteboard/fastboard/web#ui)
- [`FastboardApp`](/zh-CN/api-reference/whiteboard/fastboard/web#fastboardapp)
- [`FastboardUIConfig`](/zh-CN/api-reference/whiteboard/fastboard/web#fastboarduiconfig)
- [`ToolbarConfig`](/zh-CN/api-reference/whiteboard/fastboard/web#toolbarconfig)

<a id="内置控件源码"></a>

### 内置 UI 控件源码 [#内置-ui-控件源码]

- 工具条控件：[Contents.svelte](https://github.com/netless-io/fastboard/blob/main/packages/fastboard-ui/src/components/Toolbar/components/Contents.svelte)
- 撤回/重做控件：[UndoRedo.svelte](https://github.com/netless-io/fastboard/blob/main/packages/fastboard-ui/src/components/RedoUndo/RedoUndo.svelte)
- 缩放控件：[ZoomControl.svelte](https://github.com/netless-io/fastboard/blob/main/packages/fastboard-ui/src/components/ZoomControl/ZoomControl.svelte)
- 翻页控件：[PageControl.svelte](https://github.com/netless-io/fastboard/blob/main/packages/fastboard-ui/src/components/PageControl/PageControl.svelte)
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="ios" />
Fastboard SDK 提供了丰富的内置 UI 控件，包括工具条控件、撤回/重做控件、缩放控件和翻页控件。这些控件支持一些轻量化的配置，例如隐藏/展示控件等。如果你的业务场景需要用到更多功能，你可以自行实现控件并添加到白板中。

<DocsImage alt="Fastboard 内置 UI 控件示意图" src="https://web-cdn.agora.io/docs-portal/img/whiteboard/ui-widgets.png" displayWidth="55%" />

本文介绍如何使用 Fastboard SDK 修改白板内置 UI 控件的配置，以及如何将自定义的 UI 控件添加到白板中。

## 前提条件 [#前提条件-1]

开始前，请确保你已在项目中集成声网 Fastboard SDK 并加入房间，详见[加入白板房间](/zh-CN/realtime-media/whiteboard/fastboard-sdk/get-started/join-room-fb)。

## 实现方法 [#实现方法-1]

### 修改内置 UI 控件配置 [#修改内置-ui-控件配置-1]

你可以在创建 Fastboard UI 对象后，通过修改 [`FastboardUIConfig`](/zh-CN/api-reference/whiteboard/fastboard/web#fastboarduiconfig) 对白板内置 UI 控件进行一些简单的配置，例如隐藏/展示控件。参考以下代码：

```javascript tab="JavaScript" tabGroup="language"
// 创建 ui 实例
const ui = createUI(fastboard, container);
// 更新工具条配置
ui.update({ config: {  
  toolbar: {
    enable: true,
    placement: 'left',
    items: ["clicker", "selector", "pencil", "text", "shapes", "eraser", "clear"],
    apps: { enable: true },
  },
  redo_undo: { enable: true },
  zoom_control: { enable: true },
  page_control: { enable: true }, } });

// 隐藏缩放控件
ui.update({ config: { zoom_control: { enable: false } } });

// 设置工具条仅展示铅笔和橡皮工具
const toolbar_items = ["pencil", "eraser"];
ui.update({ config: { toolbar: { items: toolbar_items } } });
```

```jsx tab="React" tabGroup="language"
// 控件默认配置
<Fastboard app={fastboard} config={{
  toolbar: {
    enable: true,
    placement: 'left',
    items: ["clicker", "selector", "pencil", "text", "shapes", "eraser", "clear"],
    apps: { enable: true },
  },
  redo_undo: { enable: true },
  zoom_control: { enable: true },
  page_control: { enable: true },
}} />;

// 隐藏缩放控件
<Fastboard app={fastboard} config={{ zoom_control: { enable: false } }} />;

// 设置工具条仅展示铅笔和橡皮工具
const toolbar_items = ["pencil", "eraser"];
<Fastboard app={fastboard} config={{ toolbar: { items: toolbar_items } }} />;
```

### 使用自定义 UI 控件 [#使用自定义-ui-控件-1]

以定制工具条控件为例，你可以参考以下步骤将你自行实现的工具条控件添加到白板中。

#### 禁用内置工具条 [#禁用内置工具条-1]

通过修改 [`FastboardUIConfig`](/zh-CN/api-reference/whiteboard/fastboard/web#fastboarduiconfig) 中的 `toolbar` 配置项，禁用 Fastboard 内置的工具条：

```javascript tab="JavaScript" tabGroup="language"
const ui = createUI(fastboard, container);
ui.update({ config: {
  // highlight-start
  toolbar: { enable: false },
  // highlight-end
  redo_undo: { enable: true },
  zoom_control: { enable: true },
  page_control: { enable: true },
  } });
```

```jsx tab="React" tabGroup="language"
<Fastboard app={fastboard} config={{
  // highlight-start
  toolbar: { enable: false },
  // highlight-end
  redo_undo: { enable: true },
  zoom_control: { enable: true },
  page_control: { enable: true },
}} />;
```

#### 添加自定义 UI 控件 [#添加自定义-ui-控件-1]

参考以下步骤将自定义 UI 控件添加至白板：

1. 自行开发自定义 UI 控件。
2. 将控件和 Fastboard 组件放置于同一个 HTML 容器中。
3. 向自定义 UI 控件传入 Fastboard 实例对象以调用控件实现的逻辑。

:::info[信息]
- 你可以在自定义 UI 控件的代码中调用 `FastboardApp` 类下的 API 来实现工具切换、缩放、翻页等功能。调用这些 API 需要房间具有读写权限，详见[开发注意事项](#开发注意事项-1)。
- 我们提供了 Fastboard 内置的四种 UI 控件的实现代码供你参考，详见[内置控件源码](#内置-ui-控件源码-1)。
:::

以下示例创建了一个自定义工具条控件，并将其关联到白板中：

```javascript tab="JavaScript" tabGroup="language"
import { createFastboard, createUI } from "@netless/fastboard";

async function main() {
  const fastboard = await createFastboard(...);
  const container = createContainer();
  const ui = createUI(fastboard, container);
  ui.update({ config: { ...ui_config } });
  fastboard.destroy();
}
function createContainer() {
  const container = document.createElement("div");
  Object.assign(container.style, {
    height: "400px",
    border: "1px solid",
    background: "#f1f2f3",
  });
  // highlight-start
  document.body.appendChild(container);
  container.appendChild(createCustomToolbar());
  return container;
}
function createCustomToolbar() {
  const customToolbar = document.createEgetlement("div");
  // 自定义的工具条逻辑
  // ·····
  return customToolbar;
}
// highlight-end
```

```jsx tab="React" tabGroup="language"
import { useFastboard, Fastboard } from "@netless/fastboard-react";
import React from "react";
import { createRoot } from "react-dom/client";

function App() {
  const fastboard = useFastboard(...);
  return (
    <div
      style={{
        height: "400px",
        border: "1px solid",
        background: "#f1f2f3",
      }}
    >
      // highlight-start
      <Fastboard app={fastboard} />
      
      <CustomToolbar  app={fastboard} />
      // highlight-end
    </div>
  );
}

createRoot(document.getElementById("app")).render(<App />);
```

## 参考信息 [#参考信息-1]

### 开发注意事项 [#开发注意事项-1]

自定义 UI 控件逻辑中，调用 `FastboardApp` 类下的 API 时，例如 `setAppliance`，需要注意在读写权限下调用。你可以通过监听 `FastboardApp` 实例对象的 `writable` 属性获取当前是否具备读写权限。

```javascript
let app = await createFastboard({ ...config });
app.writable; // { value: true, subscribe/reaction, set? }
```

- `value`：直接取到当前值。
  - `true`：互动模式，即对白板具有读写权限。
  - `false`：订阅模式，即对白板具有只读权限。
- `subscribe`/`reaction`：设置一个监听器，监听权限值的变化。方法中的回调函数会在每次值发生变化时被调用，输出新的值，并返回一个停止监听的函数。其中，调用 `subscribe` 方法会立即执行一次，输出当前权限值，`reaction` 方法则不会。
- `set`：修改权限值。注意，修改不一定会同步更新到 `value` 上，当前权限值以 `value` 的值为准。

下面的示例代码中展示了两种监听方法的区别，并通过调用 `dispose` 取消 `subscribe` 注册的监听器：

```javascript
let dispose = app.writable.subscribe(value => {
  console.log("writable1:", value);
}); // 控制台输出 writable1: true
app.writable.reaction(value => {
  console.log("writable2:", value);
}); // 控制台无输出

app.writable.set(false); // 控制台输出 writable1: false, writable2: false

// 取消 subscribe 注册的监听器
dispose();

app.writable.set(true); // 控制台输出 writable2: true
```

### API 参考 [#api-参考-1]

- [`createUI`](/zh-CN/api-reference/whiteboard/fastboard/web#createui)
- [`UI`](/zh-CN/api-reference/whiteboard/fastboard/web#ui)
- [`FastboardApp`](/zh-CN/api-reference/whiteboard/fastboard/web#fastboardapp)
- [`FastboardUIConfig`](/zh-CN/api-reference/whiteboard/fastboard/web#fastboarduiconfig)
- [`ToolbarConfig`](/zh-CN/api-reference/whiteboard/fastboard/web#toolbarconfig)

### 内置 UI 控件源码 [#内置-ui-控件源码-1]

- 工具条控件：[Contents.svelte](https://github.com/netless-io/fastboard/blob/main/packages/fastboard-ui/src/components/Toolbar/components/Contents.svelte)
- 撤回/重做控件：[UndoRedo.svelte](https://github.com/netless-io/fastboard/blob/main/packages/fastboard-ui/src/components/RedoUndo/RedoUndo.svelte)
- 缩放控件：[ZoomControl.svelte](https://github.com/netless-io/fastboard/blob/main/packages/fastboard-ui/src/components/ZoomControl/ZoomControl.svelte)
- 翻页控件：[PageControl.svelte](https://github.com/netless-io/fastboard/blob/main/packages/fastboard-ui/src/components/PageControl/PageControl.svelte)
<_PlatformProcessedMarker close="true" />

<_PlatformProcessedMarker groupMode="structured" canonicalPlatform="web" platform="web" />
Fastboard SDK 提供了丰富的内置 UI 控件，包括工具条控件、撤回/重做控件、缩放控件和翻页控件。这些控件支持一些轻量化的配置，例如隐藏/展示控件等。如果你的业务场景需要用到更多功能，你可以自行实现控件并添加到白板中。

<DocsImage alt="Fastboard 内置 UI 控件示意图" src="https://web-cdn.agora.io/docs-portal/img/whiteboard/ui-widgets.png" displayWidth="55%" />

本文介绍如何使用 Fastboard SDK 修改白板内置 UI 控件的配置，以及如何将自定义的 UI 控件添加到白板中。

## 前提条件 [#前提条件-2]

开始前，请确保你已在项目中集成声网 Fastboard SDK 并加入房间，详见[加入白板房间](/zh-CN/realtime-media/whiteboard/fastboard-sdk/get-started/join-room-fb)。

## 实现方法 [#实现方法-2]

### 修改内置 UI 控件配置 [#修改内置-ui-控件配置-2]

你可以在创建 Fastboard UI 对象后，通过修改 [`FastboardUIConfig`](/zh-CN/api-reference/whiteboard/fastboard/web#fastboarduiconfig) 对白板内置 UI 控件进行一些简单的配置，例如隐藏/展示控件。参考以下代码：

```javascript tab="JavaScript" tabGroup="language"
// 创建 ui 实例
const ui = createUI(fastboard, container);
// 更新工具条配置
ui.update({ config: {  
  toolbar: {
    enable: true,
    placement: 'left',
    items: ["clicker", "selector", "pencil", "text", "shapes", "eraser", "clear"],
    apps: { enable: true },
  },
  redo_undo: { enable: true },
  zoom_control: { enable: true },
  page_control: { enable: true }, } });

// 隐藏缩放控件
ui.update({ config: { zoom_control: { enable: false } } });

// 设置工具条仅展示铅笔和橡皮工具
const toolbar_items = ["pencil", "eraser"];
ui.update({ config: { toolbar: { items: toolbar_items } } });
```

```jsx tab="React" tabGroup="language"
// 控件默认配置
<Fastboard app={fastboard} config={{
  toolbar: {
    enable: true,
    placement: 'left',
    items: ["clicker", "selector", "pencil", "text", "shapes", "eraser", "clear"],
    apps: { enable: true },
  },
  redo_undo: { enable: true },
  zoom_control: { enable: true },
  page_control: { enable: true },
}} />;

// 隐藏缩放控件
<Fastboard app={fastboard} config={{ zoom_control: { enable: false } }} />;

// 设置工具条仅展示铅笔和橡皮工具
const toolbar_items = ["pencil", "eraser"];
<Fastboard app={fastboard} config={{ toolbar: { items: toolbar_items } }} />;
```

### 使用自定义 UI 控件 [#使用自定义-ui-控件-2]

以定制工具条控件为例，你可以参考以下步骤将你自行实现的工具条控件添加到白板中。

#### 禁用内置工具条 [#禁用内置工具条-2]

通过修改 [`FastboardUIConfig`](/zh-CN/api-reference/whiteboard/fastboard/web#fastboarduiconfig) 中的 `toolbar` 配置项，禁用 Fastboard 内置的工具条：

```javascript tab="JavaScript" tabGroup="language"
const ui = createUI(fastboard, container);
ui.update({ config: {
  // highlight-start
  toolbar: { enable: false },
  // highlight-end
  redo_undo: { enable: true },
  zoom_control: { enable: true },
  page_control: { enable: true },
  } });
```

```jsx tab="React" tabGroup="language"
<Fastboard app={fastboard} config={{
  // highlight-start
  toolbar: { enable: false },
  // highlight-end
  redo_undo: { enable: true },
  zoom_control: { enable: true },
  page_control: { enable: true },
}} />;
```

#### 添加自定义 UI 控件 [#添加自定义-ui-控件-2]

参考以下步骤将自定义 UI 控件添加至白板：

1. 自行开发自定义 UI 控件。
2. 将控件和 Fastboard 组件放置于同一个 HTML 容器中。
3. 向自定义 UI 控件传入 Fastboard 实例对象以调用控件实现的逻辑。

:::info[信息]
- 你可以在自定义 UI 控件的代码中调用 `FastboardApp` 类下的 API 来实现工具切换、缩放、翻页等功能。调用这些 API 需要房间具有读写权限，详见[开发注意事项](#开发注意事项-2)。
- 我们提供了 Fastboard 内置的四种 UI 控件的实现代码供你参考，详见[内置控件源码](#内置-ui-控件源码-2)。
:::

以下示例创建了一个自定义工具条控件，并将其关联到白板中：

```javascript tab="JavaScript" tabGroup="language"
import { createFastboard, createUI } from "@netless/fastboard";

async function main() {
  const fastboard = await createFastboard(...);
  const container = createContainer();
  const ui = createUI(fastboard, container);
  ui.update({ config: { ...ui_config } });
  fastboard.destroy();
}
function createContainer() {
  const container = document.createElement("div");
  Object.assign(container.style, {
    height: "400px",
    border: "1px solid",
    background: "#f1f2f3",
  });
  // highlight-start
  document.body.appendChild(container);
  container.appendChild(createCustomToolbar());
  return container;
}
function createCustomToolbar() {
  const customToolbar = document.createEgetlement("div");
  // 自定义的工具条逻辑
  // ·····
  return customToolbar;
}
// highlight-end
```

```jsx tab="React" tabGroup="language"
import { useFastboard, Fastboard } from "@netless/fastboard-react";
import React from "react";
import { createRoot } from "react-dom/client";

function App() {
  const fastboard = useFastboard(...);
  return (
    <div
      style={{
        height: "400px",
        border: "1px solid",
        background: "#f1f2f3",
      }}
    >
      // highlight-start
      <Fastboard app={fastboard} />
      
      <CustomToolbar  app={fastboard} />
      // highlight-end
    </div>
  );
}

createRoot(document.getElementById("app")).render(<App />);
```

## 参考信息 [#参考信息-2]

### 开发注意事项 [#开发注意事项-2]

自定义 UI 控件逻辑中，调用 `FastboardApp` 类下的 API 时，例如 `setAppliance`，需要注意在读写权限下调用。你可以通过监听 `FastboardApp` 实例对象的 `writable` 属性获取当前是否具备读写权限。

```javascript
let app = await createFastboard({ ...config });
app.writable; // { value: true, subscribe/reaction, set? }
```

- `value`：直接取到当前值。
  - `true`：互动模式，即对白板具有读写权限。
  - `false`：订阅模式，即对白板具有只读权限。
- `subscribe`/`reaction`：设置一个监听器，监听权限值的变化。方法中的回调函数会在每次值发生变化时被调用，输出新的值，并返回一个停止监听的函数。其中，调用 `subscribe` 方法会立即执行一次，输出当前权限值，`reaction` 方法则不会。
- `set`：修改权限值。注意，修改不一定会同步更新到 `value` 上，当前权限值以 `value` 的值为准。

下面的示例代码中展示了两种监听方法的区别，并通过调用 `dispose` 取消 `subscribe` 注册的监听器：

```javascript
let dispose = app.writable.subscribe(value => {
  console.log("writable1:", value);
}); // 控制台输出 writable1: true
app.writable.reaction(value => {
  console.log("writable2:", value);
}); // 控制台无输出

app.writable.set(false); // 控制台输出 writable1: false, writable2: false

// 取消 subscribe 注册的监听器
dispose();

app.writable.set(true); // 控制台输出 writable2: true
```

### API 参考 [#api-参考-2]

- [`createUI`](/zh-CN/api-reference/whiteboard/fastboard/web#createui)
- [`UI`](/zh-CN/api-reference/whiteboard/fastboard/web#ui)
- [`FastboardApp`](/zh-CN/api-reference/whiteboard/fastboard/web#fastboardapp)
- [`FastboardUIConfig`](/zh-CN/api-reference/whiteboard/fastboard/web#fastboarduiconfig)
- [`ToolbarConfig`](/zh-CN/api-reference/whiteboard/fastboard/web#toolbarconfig)

### 内置 UI 控件源码 [#内置-ui-控件源码-2]

- 工具条控件：[Contents.svelte](https://github.com/netless-io/fastboard/blob/main/packages/fastboard-ui/src/components/Toolbar/components/Contents.svelte)
- 撤回/重做控件：[UndoRedo.svelte](https://github.com/netless-io/fastboard/blob/main/packages/fastboard-ui/src/components/RedoUndo/RedoUndo.svelte)
- 缩放控件：[ZoomControl.svelte](https://github.com/netless-io/fastboard/blob/main/packages/fastboard-ui/src/components/ZoomControl/ZoomControl.svelte)
- 翻页控件：[PageControl.svelte](https://github.com/netless-io/fastboard/blob/main/packages/fastboard-ui/src/components/PageControl/PageControl.svelte)
<_PlatformProcessedMarker close="true" />
</_PlatformTabsGroup>
