# forge-whiteboard

应用于 `forge-room` 房间的白板应用。请先[参考]()创建房间实例, 然后才能加载白板应用。

## 启动白板

```javascript
const whiteboard = await room.applicationManager.launchApplication(WhiteboardApplication, {
    width: 1920,
    height: 1080,
    defaultToolbarStyle: {
        tool: "curve"
    }
}, "MainWhiteboard");

// 应用启动之后可以进行样式配置, 并挂载到 dom 上
whiteboard.view.style.height = `100vh`;
whiteboard.view.style.width = `100vw`;
whiteboard.view.style.background = "#9E9E9E";

document.body.appendChild(whiteboard.view);
```

### 启动配置项

`room.launchApplication` 第二个参数为白板的启动配置, 需注意同一个房间的不同客户端应使用一致的配置项来启动同一个 appId 对应的白板应用, 推荐
将启动配置硬编码在源代码中. 配置项详细说明如下:

**白板配置 WhiteboardOption**

| 属性名           | 类型      | 默认值   | 说明         |
|---------------|---------|-------|------------|
| width         | number  | 无     | 可视区域的宽度    |
| height        | number  | 无     | 可视区域的高度    |
| defaultToolbarStyle | ToolbarStyle | 见下文   | 默认工具栏样式    |
| maxScaleRatio | maxScaleRatio | -1    | 参考[相机]()一节 |
| stretchToFill | boolean | false | 忽略宽高比, 白板可绘制区域会撑满容器 |

**工具栏配置 ToolbarStyle**

| 属性名 | 类型| 默认值 | 说明 |
| --- | --- | --- | --- |
| tool | WhiteboardToolType | "curve" | 当前工具 |
| strokeColor | string | "#009688" | 描边颜色 |
| fillColor | string / null | null | 填充颜色, null 表示无填充 |
| fontSize | number | 24 | 字体大小 |
| fontFamily | string | "Courier New" | 字体名称 |
| strokeWidth | number | 4 | 描边宽度 |
| dashArray | number[] | [] | 虚线样式, 默认为 [] 表示实线 |

dashArray 配置参考 [MDN](https://developer.mozilla.org/zh-CN/docs/Web/API/CanvasRenderingContext2D/setLineDash)

## 工具栏

房间内每个用户都可以独立的设置工具栏样式, 并且设置后的值会持久的保证在房间存储中。直接通过 `whiteboard.tool` 等属性可以获取和设置当前工具栏样式。

```javascript

whiteboard.tool = "curve";
whiteboard.strokeColor = "#009688";
whiteboard.fillColor = "#fff";
whiteboard.fontSize = 24;
whiteboard.fontFamily = "Courier New";
whiteboard.strokeWidth = 4;
whiteboard.dashArray = [5,2,5];
```

通过 `WhiteboardEvents.toolbarStyleChange` 事件可以监听工具栏样式的变化, 方便跟新 UI.

```javascript
whiteboard.on(WhiteboardEvents.toolbarStyleChange, (style) => {
    console.log(style);
});
```

    "curve"
    | "rectangle"
    | "selector"
    | "line"
    | "arrow"
    | "text"
    | "ellipse"
    | "triangle"
    | "eraser"
    | "laser"
    | "grab";

### 工具

`WhiteboardToolType` 类型定义了白板的工具类型, 目前支持以下工具:

| 名称 | 说明 |
| --- | --- |
| curve | 自由曲线 |
| rectangle | 矩形 |
| selector   | 选择工具 |
| line | 直线 |
| arrow  | 箭头连接线 |
| text | 文本 |
| ellipse | 椭圆(圆) |
| triangle | 三角形 |
| eraser | 擦除工具 |
| laser | 激光笔 |
| grab | 拖动工具 |

### 元素交互

工具栏控制新增元素的样式, 每个元素也可以独立的修改各类样式.

首先需要通过 `WhiteboardEvents.elementSelected` 事件监听元素被选中, 然后通过 `whiteboard.setElementAttribute` 修改元素样式.

```javascript
whiteboard.on(WhiteboardEvents.elementSelected, (userId, pageId, elementId, attributes, boundingRect) => {
    console.log(userId); // 选中的元素是谁创建的
    console.log(pageId); // 元素所在页码
    console.log(elementId); // 元素 id
    console.log(attributes); // 元素属性列表, 不同元素的可修改属性不同
    console.log(boundingRect); // 元素边界, 数组中的四个值分别对应 x, y, width, height
});
```

`forge-whiteboard` 不内置 UI, 你需要自行决定元素被选中后的 UI 交互, 可以在屏幕的固定位置显示一个元素属性面板, 也可以在元素附近(上方)显示元素属性面板.
如果是想要在元素附近显示属性面板, `WhiteboardEvents.elementSelected` 事件中的 `boundingRect` 可用于帮助定位属性面板的位置.

可以通过 `whiteboard.getElementAttribute` 来确定元素当前属性值, 以绘制元素属性面板. 注意不同元素的可修改属性不同, "curve" 工具绘制的元素就不存在 "fontFamily" 属性,
通过 `WhiteboardEvents.elementSelected` 事件中第四个参数来确定可修改属性列表.

```javascript
// 获取元素属性
const strokeColor = whiteboard.getElementAttribute(pageId, elementId, "strokeColor");
const fontFamily = whiteboard.getElementAttribute(pageId, elementId, "fontFamily");

// 设置元素属性

whiteboard.setElementAttribute(pageId, elementId, "strokeColor", "#fff");
whiteboard.setElementAttribute(pageId, elementId, "fontFamily", "kai");
```

## 页面操作

**基础页面操作**

`forge-whiteboard` 页面只有一个 id 属性, 页面之间没有前后顺序的关系, 可通过以下 api 进行页面的跳转等操作，注意需要有对应的页面操作权限.

```javascript
// 添加页面
whiteboard.addPage("page/1");
// 跳转至刚添加的页面
whiteboard.gotoPage("page/1");
// 删除页面
whiteboard.deletePage("page/1");
// 获取页面列表
whiteboard.pageList();
// 获取当前页面 id, 依据视角参数不同用户可能在不同页面, 通过 userId 获取指定用户的当前页面 id, 不传 userId 参数则获取自己的当前页面 id
whiteboard.currentPageId("userId");
```

**顺序页面操作**

为了支持诸如上一页、下一页等功能, `forge-whiteboard` 通过 `whiteboard.indexedNavigation` 对象提供此类 api.

```javascript

// 跳转
whiteboard.indexedNavigation.nextPage();
whiteboard.indexedNavigation.prevPage();

// 添加和插入
whiteboard.indexedNavigation.pushPage();
whiteboard.indexedNavigation.insertPage(3); // 在第三页后插入新页面

// 删除
whiteboard.indexedNavigation.deletePage(3);

// 总页数
whiteboard.indexedNavigation.pageCount;

// 当前页发生变化时触发
whiteboard.indexedNavigation.on("activePageChange", pageIndex => {
    console.log(pageIndex);
});

// 总页数发生变化时触发
whiteboard.indexedNavigation.on("pageCountChange", pageCount => {
    console.log(pageCount);
});
```

**其他页面操作**

```javascript

// 复制页面
whiteboard.clonePage("1", "2");

// 清空页面内容
whiteboard.clearPage();
```

## 白板权限

`forge-whiteboard` 设计了细致的白板权限控制机制, 通过不同权限配置可以实现很多有趣的场景. 默认没有给任何权限.

| 权限类型 | 描述                                   |
|---|--------------------------------------|
|WhiteboardPermissionFlag.none| 没有任何权限, 只能同步的看他人绘制                   |
|WhiteboardPermissionFlag.draw| 可以绘制元素                               |
|WhiteboardPermissionFlag.editSelf| 可以修改自己创建的元素                          |
|WhiteboardPermissionFlag.editOthers| 可以修改他人创建的元素                          |
|WhiteboardPermissionFlag.deleteSelf| 可以删除自己创建的元素                          |
|WhiteboardPermissionFlag.deleteOthers| 可以删除他人创建的元素                          |
|WhiteboardPermissionFlag.mainView| 可以修改主视角, 包括切换页面, 拖动和缩放画布, 添加页面, 删除页面 |
|WhiteboardPermissionFlag.setOthersView| 可以修改他人的视角模式, 指定他人的页码                 |
|WhiteboardPermissionFlag.all| 拥有所有权限                               |

### 权限相关 api

```javascript
// 给当前客户端用户设置权限
whiteboard.permissions.addPermission(WhiteboardPermissionFlag.all);

// 给指定用户设置权限
whiteboard.permissions.addPermission(WhiteboardPermissionFlag.all, "userId");

// 获取当前权限列表
whiteboard.permissions.getPermissionFlags();

// 获取当前权限值, 可以用来判断是否有某个权限
const permissionValue = whiteboard.permissions.getPermissionValue();
// 是否有绘制权限
permissionValue & WhiteboardPermissionFlag.draw;
```

## 相机

`forge-whiteboard` 通过相机的概念来控制试图的拖动和缩放, 以及配置白板画布的范围.

### 相机相关配置

**WhiteboardOption.maxScaleRatio** 设置画布最大区域在默认高宽上的放大比例. 默认值为 -1, 表示无边界. 只能设置 -1 或者 >= 1 的值.  
例如: `WhiteboardOption.width` 设置为 1920, `WhiteboardOption.height` 设置为 1080, `WhiteboardOption.maxScaleRatio` 设置为 2, 则画布最大区域为 3840 x 2160. 拖动和缩放操作都会受最大区域限制.

**Whiteboard.enableCameraBoundaryHighlight** 控制相机超出边界时是否显示向内的高亮阴影以做警示. 默认值为 true.

**Whiteboard.cameraBoundaryColor** 设置警示颜色. 默认值为 "#F44336".

**Whiteboard.enableCameraByMouse** 控制是否允许通过鼠标/触摸板控制相机的缩放和拖动. 默认值为 true.

**Whiteboard.enableCameraByTouch** 控制是否允许通过触摸屏上的触摸手势控制相机的缩放和拖动. 默认值为 true.

```typescript
// 设置最高 2 倍放大
const whiteboard = await room.applicationManager.launchApplication(WhiteboardApplication, {
    width: 1920,
    height: 1080,
    maxScaleRatio: 2,
    defaultToolbarStyle: {
        tool: "curve"
    }
}, "MainWhiteboard");

// 取消相机超出边界时的高亮显示
whiteboard.enableCameraBoundaryHighlight = false;
// 禁止通过触摸手势控制相机
whiteboard.enableCameraByMouse = false;
```

### 通过代码控制相机

**Whiteboard.translateCamera** 拖动相机  
**Whiteboard.scaleCamera** 以画布中心为不动点缩放相机  
**Whiteboard.resetCamera** 重置相机位置和缩放  

```typescript
whiteboard.scaleCamera(1.2);
whiteboard.translateCamera(10, 10);
whiteboard.resetCamera();
```

## 其他

### 显示和隐藏用户光标

用户光标是以用户主题色为背景颜色内填用户名信息的圆角矩形, 根据绘制图形不同位置不一样，用于指示当前操作人员。

通过 `Whiteboard.showLiveCursor` 控制是否显示用户光标, 默认值为 false.