当加入一个实时房间之前,你需要构造一个 room
对象。你可以阅读进阶教程的《实时房间》篇章来对该过程有个高屋建瓴的理解。
你可以通过如下代码加入房间,并在回调方法中拿到 room
对象。
var joinRoomParams = {...};
var roomCallbacks = {...};
whiteWebSdk.joinRoom(joinRoomParams, roomCallbacks)
.then(function (room) {
// 加入房间成功,拿到 room 对象
})
.catch(function (error) {
// 加入房间失败,拿到 error 对象
});
其中,joinRoom
的签名是这样的。
interface Room {
joinRoom(params: JoinRoomParams, callbacks?: Partial<RoomCallbacks>): Promise<Room>
};
方法的第二参数 callbacks?: Partial<RoomCallbacks>
是初始化的回调函数,我们会在本章节最后的一部分讲解,此处按下不表。
方法的第一参数的类型 JoinRoomParams
是加入房间中最重要的部分,其定义如下。
type JoinRoomParams = {
readonly uuid: string;
readonly uid: string;
readonly roomToken: string;
readonly userPayload?: any;
readonly isWritable?: boolean;
readonly disableDeviceInputs?: boolean;
readonly disableCameraTransform?: boolean;
readonly enableDrawPoint?: boolean;
readonly disableNewPencil?: boolean;
readonly disableAutoResize?: boolean;
readonly cursorAdapter?: CursorAdapter;
readonly cameraBound?: CameraBound;
readonly disableEraseImage?: boolean;
readonly floatBar?: boolean | Partial<FloatBarOptions>;
readonly hotKeys?: Partial<HotKeys>;
readonly invisiblePlugins?: ReadonlyArray<InvisiblePluginClass<string, any>>;
readonly wrappedComponents?: WrappedComponents;
readonly rejectWhenReadonlyErrorLevel?: RoomErrorLevel;
};
房间的 uuid
,在构造房间时会返回,是该房间的唯一标识符。
加入房间的用户唯一标识符,类型为字符串。这个值是自定义的,应该将其设置为用户系统的用户 ID。
房间的 Token,用于鉴权。阅读《访问密钥|项目与鉴权》以便了解如何签出房间的 Token。
可以是任意类型的数据结构,用于描述当前用户的自定义信息。房间内用户在加入房间设置的 userPayload
可被房间内其他用户通过如下代码读取。
for (var member of room.state.roomMembers) {
// 读取房间内某个用户的 userPayload 字段
console.log(member.userPayload);
}
特别的,如果 userPayload
的类型为 object,并且包含 userId
字段,则该字段会被包含在每一条日志中并被上报(如果没有关闭日志上报功能的话)。
是否以可写模式加入房间,默认是 true
,若为 false
则为只读模式。注意区分「只读模式」与「禁止设备操作」,具体参考进阶教程《只读模式|禁止操作》。
是否禁止设备操作,默认是 false
。注意区分「只读模式」与「禁止设备操作」,具体参考进阶教程《禁止设备操作|禁止操作》。
默认值为 false
。表明是否允许使用用 pencil
工具画点。如果设置成 false
,则当使用 pencil
工具单击白板绘制的点不会保留在屏幕上。该参数只有在 disableNewPencil
的值为 false
时才会起作用。
默认值为 true
。是否禁止 2.12.5 以及之后版本的带笔锋的铅笔工具(参考 《版本历史 - 2.12.5》)。若为 true
,则使用 2.12.5 之前的没有笔锋的铅笔工具。
房间内任意一个用户开启笔锋后,如果绘制了任何笔迹,都会导致房间内所有 2.12.5 以下版本的用户控制台报错。此外,绘制过任何带笔锋笔迹的房间所产生了录像,若使用 2.12.5 以下版本播放,也会报错。为了避免问题,要么保证同一个房间的所有用户都升级到了 2.12.5 及以上版本,要么将所有的 2.12.5 及以上版本的用户关闭笔锋功能。
在用选择工具(currentApplianceName="selector"
)选中物体后,出现的浮动条。
若为 false
(默认值),则永远不会出现浮动条。若为 true
,则会出现浮动条。此外,还可以为 FloatBarOptions
类型的 object,其定义如下。
type FloatBarOptions = {
// 浮动条调色盘的颜色列表
readonly colors?: readonly Readonly<Color>;
}
// 由 3 到 4 个 0 ~ 255 分量组成的数组,分别表示 R、G、B、A 的取值
type Color = readonly number[];
用于配置热键。若不传,则使用默认热键方案,具体如下。
键盘按键 | 效果 |
---|---|
Backspace / Delete | 删除所选对象 |
Shift | 锁定放缩长宽比,令其等比放缩 |
ctrl + z / command + z | 撤回 |
ctrl + y / command + y | 重做 |
ctrl + c / command + c | 复制 |
ctrl + v / command + v | 粘贴 |
如果你想关闭热键功能,可以将其值置为 {}
。hotKeys
的类型为 Partial<HotKeys>
,其定义如下。
type HotKeys = {
// 复刻
readonly duplicate: HotKey;
// 复制
readonly copy: HotKey;
// 粘贴
readonly paste: HotKey;
// 撤回
readonly undo: HotKey;
// 重做
readonly redo: HotKey;
// 删除
readonly delete: HotKey;
// 锁定放缩比例
readonly lock: HotKey;
// 切换到选择工具(selector)
readonly changeToSelector: HotKey;
// 切换到激光笔(laserPointer)
readonly changeToLaserPointer: HotKey;
// 切换到铅笔工具(pencil)
readonly changeToPencil: HotKey;
// 切换到矩形工具(rectangle)
readonly changeToRectangle: HotKey;
// 切换到圆形工具(ellipse)
readonly changeToEllipse: HotKey;
// 切换到橡皮工具(eraser)
readonly changeToEraser: HotKey;
// 切换到直线工具(straight)
readonly changeToStraight: HotKey;
// 切换到箭头工具(arrow)
readonly changeToArrow: HotKey;
// 切换到抓手工具(handle)
readonly changeToHand: HotKey;
};
// 键盘类型
enum KeyboardKind {
Mac = "mac", // mac 的键盘
Windows = "windows", // windows 的键盘
}
// 热键类型,作为 HotKeys 的值
type HotKey = string | HotKeyDescription | ReadonlyArray<string | HotKeyDescription> | HotKeyChecker;
type HotKeyDescription = {
// 键盘按键
readonly key: string;
// 是否同时按下 alt 键
readonly altKey: boolean | null;
// 是否同时按下 ctrl 键
readonly ctrlKey: boolean | null;
// 是否同时按下 shift 键
readonly shiftKey: boolean | null;
};
// 检查是否满足热键条件
type HotKeyChecker = (event: HotKeyEvent, kind: KeyboardKind) => boolean;
type HotKeyEvent = {
// JavaScript 的原生键盘事件
readonly nativeEvent?: KeyboardEvent;
// 按下或松开
readonly kind: "KeyDown" | "KeyUp";
// 键盘按键
readonly key: string;
// 是否按下了 alt 键
readonly altKey: boolean;
// 是否按下了 ctrl 键
readonly ctrlKey: boolean;
// 是否按下了 shift 键
readonly shiftKey: boolean;
};
例如,如下几种方式,都可以将 ctrl + c 绑定到「撤回」按键。
// 基于 HotKeyDescription 定义
whiteWebSdk.joinRoom({
uuid: "$uuid",
roomToken: "$roomToken",
hotKeys: {
undo: {key: "c", ctrlKey: true},
},
});
// 基于 HotKeyChecker 定义
whiteWebSdk.joinRoom({
uuid: "$uuid",
roomToken: "$roomToken",
hotKeys: {
undo: function(event, kind) {
return (
kind === "windows" &&
event.key === "c" &&
event.ctrlKey
);
},
},
});
决定了,在房间没有写权限时,如果主动调用了写操作,此时 SDK 应该做何响应。其类型是枚举 RoomErrorLevel
的定义如下。
enum RoomErrorLevel {
// 当无权写时执行写操作,则直接抛出错误
ThrowError = "throwError",
// 当无权写时执行写操作,拦截操作,并在控制台打印警告
Warn = "warn",
// 当无权写时执行写操作,拦截操作,什么也不做
Ignore = "ignore",
}
需要回放一段录像之前,你需要构造一个 player
对象。你可以阅读进阶教程的《回放》篇章来对该过程有个高屋建瓴的理解。
你可以通过如下代码开始回放,并在回调方法中拿到 player
对象。
var replayRoomParams = {...};
var replayCallbacks = {...};
whiteWebSdk.replayRoom(replayRoomParams, replayCallbacks)
.then(function (player) {
// 回放成功,拿到 player 对象
})
.catch(function (error) {
// 回放失败,拿到 error 对象
});
其中 replayRoom
的方法签名如下。
replayRoom(params: ReplayRoomParams, callbacks?: Partial<PlayerCallbacks>): Promise<Player>
方法的第二参数 callbacks?: Partial<PlayerCallbacks>
是初始化的回调函数,我们会在本章节最后的一部分讲解,此处按下不表。
方法的第一参数的类型 ReplayRoomParams
是加入房间中最重要的部分,其定义如下。
type ReplayRoomParams = {
readonly slice?: string;
readonly room?: string;
readonly beginTimestamp?: number;
readonly duration?: number;
readonly cursorAdapter?: CursorAdapter;
readonly cameraBound?: CameraBound;
readonly disableAutoResize?: boolean;
readonly disableCameraTransform?: boolean;
readonly invisiblePlugins?: ReadonlyArray<InvisiblePluginClass<string, any>>;
readonly wrappedComponents?: WrappedComponents;
};
指定回放特定的「录像片段」的 uuid
。可以在房间尚在录制的时候,从 room.slice
获取。
当传入该参数后,表明只回放特定片段,因此禁止再传入 room
、beginTimestamp
、duration
。
指定特定房间的 uuid
,在构造房间时会返回,是该房间的唯一标识符。传入该参数后,禁止传入 slice
参数。
如果仅传该参数,不传 beginTimestamp
和 duration
,则表明回放该房间的所有录像片段。
如果同时传入,beginTimestamp
和 duration
,则表明回放该房间在此范围之内的所有录像片段。
表明回放开始的时间戳(unix 时间,单位是毫秒)。此参数必须和 room
、duration
一起使用,且使用时禁止传入 slice
参数。
表明回放区间的时长(单位是毫秒)。此参数必须和 room
、beginTimestamp
一起使用,且使用时禁止传入 slice
参数。
无论是加入实时房间,还是回放录像,其参数 JoinRoomParams
和 ReplayRoomParams
都有一些公共字段,它们的定义如下。
设置鼠标光标适配器,参考《鼠标光标适配器》。
是否关闭「自动适配尺寸」功能,默认为 false
。关闭后,每当白板的 view 的尺寸改变时,必须主动调用 room.refreshViewSize()
以适配。
是否禁止设备主动操作视角,默认是 false
。
不可见插件列表。
默认值为 []
。这是一个装满 React.ComponentType
类型的数组,用于包装白板的 view。你可以用它对白板的 view 进行自定义包装。
视角边界,用户的视角会被限制于此范围,超出该范围后,视角会被拉回。其类型 CameraBound
定义如下。
type CameraBound = {
// 当画面突破边界时的阻尼大小,取值范围为 0.0 ~ 1.0
// 越大,阻尼越大,取 1.0 时,无论怎么拉,视角都无法突破边界
// 越小,阻尼越小,取 0.0 时,视角可以被毫无阻力地拉出边界,只有松手的时候才会弹回
// 默认值是 0.75
readonly damping?: number;
// 边界的中心点在世界坐标系中的 x 坐标
// 默认值是 0.0
readonly centerX?: number;
// 边界的中心点在世界坐标系中的 y 坐标
// 默认值是 0.0
readonly centerY?: number;
// 边界在世界坐标系中的宽,如果取 Infinity,则表示不对横向设限制
// 默认值是 Infinity
readonly width?: number;
// 边界在世界坐标系中的高,如果取 Infinity,则表示不对纵向设限制
// 默认值是 Infinity
readonly height?: number;
// 对视角放大的限制模式
// 不填是没有限制
readonly maxContentMode: ContentMode;
// 对视角缩小的限制模式
// 不填是没有限制
readonly minContentMode: ContentMode;
}
其中 ContentMode
用于设置视角对放缩的限制,我们可以通过如下方式设置。
import sdk from "white-web-sdk";
// scale 取值为 2.5
sdk.contentModeScale(2.5);
// 放缩到边界矩形的短边刚好顶住屏幕的程度
sdk.contentModeAspectFill();
// 边界矩形的短边刚好顶住屏幕的程度,在此基础上再放缩 2.5 倍
sdk.contentModeAspectFillScale(2.5);
// 放缩到刚好能在屏幕上看到完整的边界矩形的程度
sdk.contentModeAspectFit();
// 刚好能在屏幕上看到完整的边界矩形的程度,在此基础上再放缩 0.75 倍
sdk.contentModeAspectFitScale(0.75);
// 放缩到刚好能在屏幕上看到完整的边界矩形,并在周围预留 120 px 的空白空间的时候
sdk.contentModeAspectFitSpace(120);
参考之前章节中 joinRoom
和 replayRoom
的方法签名可以看到这 RoomCallbacks
、PlayerCallbacks
这两个类型。它们定义了实时房间和回放的回调方法签名,具体定义如下。
// 实时房间的回调方法签名
type RoomCallbacks = DisplayerCallbacks & {
readonly onPhaseChanged: (phase: RoomPhase) => void;
readonly onRoomStateChanged: (modifyState: Partial<RoomState>) => void;
readonly onDisconnectWithError: (error: Error) => void;
readonly onKickedWithReason: (reason: string) => void;
readonly onCanUndoStepsUpdate: (canUndoSteps: number) => void;
readonly onCanRedoStepsUpdate: (canUndoSteps: number) => void;
readonly willInterceptKeyboardEvent: (event: KeyboardEvent) => boolean;
readonly onKeyDown: (event: KeyboardEvent) => void;
readonly onKeyUp: (event: KeyboardEvent) => void;
};
// 回放录像的回调方法签名
type PlayerCallbacks = DisplayerCallbacks & {
readonly onIsPlayableChanged: (isPlayable: boolean) => void;
readonly onPhaseChanged: (phase: PlayerPhase) => void;
readonly onLoadFirstFrame: () => void;
readonly onPlayerStateChanged: (modifyState: Partial<PlayerState>) => void;
readonly onStoppedWithError: (error: Error) => void;
readonly onProgressTimeChanged: (progressTimestamp: number) => void;
};
// 实时房间与回放录像公共的回调方法签名
type DisplayerCallbacks = {
readonly onEnableWriteNowChanged: (enableWriteNow: boolean) => void;
readonly onHandToolActive: (active: boolean) => void;
readonly onSliceChanged: (slice: string) => void;
readonly onCatchErrorWhenAppendFrame: (userId: number, error: Error) => void;
readonly onCatchErrorWhenRender: (error: Error) => void;
readonly onPPTLoadProgress: (uuid: string, progress: number) => void;
readonly onPPTMediaPlay: (shapeId: string, type: MediaType) => void;
readonly onPPTMediaPause: (shapeId: string, type: MediaType) => void;
};
以「用户被提出房间」的回调方法为例。我们可以通过如下代码,在刚加入房时,注册一个回调方法。
whiteWebSdk.joinRoom({uuid: "$uuid", roomToken: "$roomToken"}, {
"onKickedWithReason": function(reason) {
console.log("kicked by server with reason:" + reason);
},
});
也可以等到房间加入成功,获取到 room
对象后,再注册回调方法。
room.callbacks.on("onKickedWithReason", function(reason) {
console.log("kicked by server with reason:" + reason);
});
也可以注销之前注册的回调方法。
function onKicked(reason) {
console.log("kicked by server with reason:" + reason);
}
// 注册回调方法
room.callbacks.on("onKickedWithReason", onKicked);
// 注销回调方法
room.callbacks.off("onKickedWithReason", onKicked);
也可以不指定回调方法,注销之前注册过的所有回调方法。
room.callbacks.off("onKickedWithReason");
也可以注册一个一次性回调,该回调会在首次调用后,自动注销自己。
room.callbacks.once("onKickedWithReason", function(reason) {
console.log("kicked by server with reason:" + reason);
});
特别的,你可以通过对 player.callbacks
做类似操作,此处就不举例了。
DisplayerCallbacks
中的方法是 room
和 player
公共的回调方法。在下文中,我会用 displayer
表示 room
或 player
。
表示 displayer.enableWriteNow
发生了改变。
表示 displayer.handToolKey
发生了改变。
displayer.slice
发生了变化。
签名为 (userId: number, error: Error) => void
。表示某个用户(userId
所指示的用户)的行为在同步过来的时候报错了。一般而言,这个报错通常是可以忽略的,具体请根据业务情况来自行决定是否监听它。
表示渲染画面时发生了错误。
加载转网页 PPT 的进度回调,签名为 (uuid: string, progress: number) => void
。其中 uuid
是 PPT 的 taskUUID
,progress
是一个 0.0 ~ 1.0 的数字,表明进度。
表明转网页 PPT 中某个 shape
对应的媒体资源开始播放了。
表明转网页 PPT 中某个 shape
对应的媒体资源暂停了。
RoomCallbacks
是 room
特有的回调方法。
表明 room.phase
发生了变化。它会传一个 phase
参数,其含义可参考《实时房间状态管理》。其类型 RoomPhase
的定义如下。
表明 room.state
发生了变化。具体参考《房间与回放的业务状态管理》。
实时房间出错了,并因此断开了连接。
被提出房间,并因此断开了连接。回调参数 reason
的值将表明被踢出去的原因。
reason 字段 |
描述 |
---|---|
kickByAdmin | 被管理员踢出 |
roomDelete | 房间被删除 |
roomZombie | 房间不活跃 |
roomBan | 房间被封禁 |
GatewayAdjust | 网关调整 |
replaceByOther | 被另一个用户顶替,当前用户被迫下线 |
crash | 服务器崩溃 |
表明 room.canUndoSteps
发生了变化。
表明 room.canRedoSteps
发生了变化。
拦截器,声明是否拦截掉白板的监听的键盘事件,若返回 true
则拦截掉。拦截掉后,该键盘事件不会引发白板的业务响应。
白板监听到了键盘按下事件。
白板监听到了键盘松开事件。
PlayerCallbacks
是 player
特有的回调方法。
表明 player.isPlayable
发生了改变。
表明 player.phase
发生了改变。
表明首帧加载完毕,该回调在 player
整个声明周期中最多调一次。在首帧加载出来之前,白板不会显示录像中的任何东西,也无法在代码中读取 player.state
。
表明 player.state
发生了变化。具体参考《房间与回放的业务状态管理》。
回放发生了错误,并因此失败,player.phase
的状态变成了 PlayerPhase.Stopped
。
表明 player.progressTime
发生了变化。