实时互动时,我们可以可以通过 cursorAdapter
自定义光标适配器。它可以在实时房间中同步、展示其他用户的鼠标光标,从而达到类似如下效果。
你可以直接在项目中安装并集成 @netless/cursor-tool
,直接让你的项目以上一章节动画中展示的效果同步光标。这是一个开源项目,你也可以从 react-whiteboard | GitHub 处获取该库的源代码。
打开 Terminal,在你项目所在目录执行如下命令,即可安装。
npm
npm install @netless/cursor-tool
yarn
yarn add @netless/cursor-tool
在加入房间之前,你可以通过如下代码绑定 @netless/cursor-tool
。
import {CursorTool} from "@netless/cursor-tool";
var cursorAdapter = new CursorTool();
var userPayload = {
cursorName: "$cursorName", // 光标上显示的该用户的名字
avatar: "$avatar_url", // 光标上用户头像的 URL 地址
};
whiteWebSdk.joinRoom({
uuid: "$uuid",
roomToken: "$roomToken",
cursorAdapter: cursorAdapter,
userPayload: userPayload,
}).then(function(room) {
// 为光标适配器绑定 room 对象
cursorAdapter.setRoom(room);
});
在开始回放录像之前,你可以通过如下代码绑定 @netless/cursor-tool
。
import {CursorTool} from "@netless/cursor-tool";
var cursorAdapter = new CursorTool();
whiteWebSdk.replayRoom({
uuid: "$uuid",
roomToken: "$roomToken",
cursorAdapter: cursorAdapter,
}).then(function(player) {
// 为光标适配器绑定 player 对象
cursorAdapter.setPlayer(player);
});
除了使用 @netless/cursor-tool
来为白板配置固定样式的光标外,我们还可以自定义光标适配器。只需要定义一个 class
,并实现接口 CursorAdapter
的全部方法,便可以根据自己的业务自行定义光标的样式了。CursorAdapter
的定义如下。
interface CursorAdapter {
createCursor(memberId: number): CursorDescription;
onAddedCursor?(cursor: Cursor): void;
onRemovedCursor?(cursor: Cursor): void;
onMovingCursor?(cursor: Cursor, positionX: number, positionY: number): void;
}
首先,我们要实现 createCursor
方法。每当某个具有「可选」权限的用户加入房间时,该方法就会被调用。我们应该令该方法返回一个 object
来描述光标的信息。该方法的签名如下。
createCursor(memberId: number): CursorDescription;
该方法会接收一个 memberId
的参数,表明该用户的 ID,返回一个类型为 CursorDescription
的对象,其定义如下。
import React from "react";
type CursorDescription = {
// 光标指针指向的点在整个光标 view 的 x 轴坐标(相对 view 的左上角)
readonly x: number;
// 光标指针指向的点在整个光标 view 的 y 轴坐标(相对 view 的左上角)
readonly y: number;
// 光标 view 的宽
readonly width: number;
// 光标 view 的高
readonly height: number;
// 初始化的 view 的 ReactNode(可选)
readonly reactNode?: React.ReactNode;
}
用户仅仅是加入房间,并不意味着会在白板上显示光标。因为用户的鼠标可能在白板之外,或光标逻辑决定隐藏光标,或用户根本就没有鼠标。总之,如下几个方法会在光标出现、消失、移动时回调。
// 光标出现
onAddedCursor?(cursor: Cursor): void;
// 光标消失
onRemovedCursor?(cursor: Cursor): void;
// 光标在移动:(positionX, positionY) 表示移动到的坐标(相对白板 view 左上角的位置)
onMovingCursor?(cursor: Cursor, positionX: number, positionY: number): void;
这几个方法都能拿到 Cursor
对象。每一个用户都有唯一一个 Cursor
对象与之对应。你可以通过调用它的方法来改变光标的外观。它的定义如下。
import React from "react";
interface Cursor {
// 用户 ID,这里亦可以唯一标识光标
readonly memberId: number;
// 光标 view 对应的 Dom 节点
readonly divElement: HTMLDivElement;
// 光标对应的用户信息
readonly cursorMember: CursorMember;
// 光标指针指向的点在整个光标 view 的 x 轴坐标(相对 view 的左上角)
readonly x: number;
// 光标指针指向的点在整个光标 view 的 y 轴坐标(相对 view 的左上角)
readonly y: number;
// 光标 view 的宽
readonly width: number;
// 光标 view 的高
readonly height: number;
// 修改它,以监听光标对应的用户信息改变
onCursorMemberChanged?: (cursorMember: CursorMember) => void;
// 传入一个 ReactNode 以定义样式
setReactNode(reactNode: React.React): void;
// 修改 cursorDescription 的特定字段
setCursorDescription(description: Partial<CursorDescription>): void;
}
// 由 3 到 4 个 0 ~ 255 分量组成的数组,分别表示 R、G、B、A 的取值
type Color = number[];
interface CursorMember {
// 当前用户笔迹的颜色
readonly color: Color;
// 当前用户教具名
readonly appliance: string;
}
如下代码可以监听 cursorMember
的变化。
cursor.onCursorMemberChanged = function(cursorMember) {
// 获得变化后的 cursorMember 对象
};
如下代码可以取消对 cursorMember
变化的监听。
cursor.onCursorMemberChanged = undefined;
如下代码可以初始化光标的外观。
var imgDom = document.createElement("img");
imgDom.src = "https://my-domain/to-my-img.png";
imgDom.width = "24px";
imgDom.height = "24px";
cursor.divElement.appendChild(imgDom);
如果使用 React,可以在 jsx
(或 tsx
)文件中使用 JSX 语法来改变光标外观。
cursor.setReactNode(
<img src="https://my-domain/to-my-img.png"
width={24} height={24}/>
);