实验API调用
提示
物理和化学学科的虚拟实验对接提供了一系列API接口,以便于您实现更多高级功能。
通常,虚拟实验对接会通过网页中的iframe
元素进行嵌入。尽管iframe
元素支持使用postMessage进行跨源通信,这种方式在需要双向数据传输时可能会变得复杂。具体来说,外部页面可以通过postMessage向iframe
内部发送信息,但iframe
内部还需要回传数据给外部页面,这同样需要使用postMessage。
为简化这一流程,我们参考了PostMate开源项目,并据此设计了一个简洁而高效的通信接口。
调用步骤
1. 安装前端SDK
- npm
- Yarn
- pnpm
- Turbo
npm install --save git+http://nobook-subject.nobook.com:10080/sdks/nobook-saas-sdk.git#v2.2.1
yarn add git+http://nobook-subject.nobook.com:10080/sdks/nobook-saas-sdk.git#v2.2.1
pnpm add git+http://nobook-subject.nobook.com:10080/sdks/nobook-saas-sdk.git#v2.2.1
turbo install --save git+http://nobook-subject.nobook.com:10080/sdks/nobook-saas-sdk.git#v2.2.1
2. 初始化通信对象
import { Postmate } from '@nobook/nobook-saas-sdk';
// 通过【实验地址获取】接口来获取物理、化学实验的地址
const experimentalURL: string = 'YOUR_EXPERIMENTAL_WEB_URL';
// 初始化
const handshake = new Postmate({
// 网页中的iframe
iframe: document.getElementById('YOUR_IFRAME_ID'),
// 是否打印log
printlog: true,
});
// 初始化iframe的src,建立通信
const communication = await handshake.init(experimentalURL);
// 监听实验场景加载完成时
communication.on('onload', async (data: {prod: string}) => {
console.log(`%c onload: ${JSON.stringify(data)}`, "color:red;");
// to do 调用API (例如:设置配置)
await communication.get('setData', experimentData);
});
// 监听内部报错
communication.on('onError', async (data: {prod: string}) => {
console.log(`%c onError: ${JSON.stringify(data)}`, "color:red;");
// to do 调用API (例如:设置配置)
await communication.get('setData', experimentData);
});
// 监听点击内部保存按钮(如需要展示内部的保存按钮请参考示例,若不需要则忽略)
communication.on("onSave", (data: {prod: string}) => {
console.log(`%c onSave: ${JSON.stringify(data)}`, "color:red;");
});
3. 调用API
// 传入的参数
const param: any = {};
// 调用暴露的API方法 'xxxx'
const result = await communication.get('xxxx', param);
API
config(data: {}): void
设置实验配置参数
参数说明
参数 | 类型 | 参数描述 |
---|---|---|
topToolbarVisible | boolean | 是否显示顶部工具栏,默认隐藏 |
titleVisible | boolean | 是否显示顶部标题,默认隐藏 |
leftToolbarVisible | boolean | 是否显示左侧工具栏(如:电学转电路图、力学绘制),默认隐藏 |
rightToolbarVisible | boolean | 是否显示右侧工具栏(如:器材库),默认隐藏 |
bottomToolbarVisible | boolean | 是否显示底部工具栏(如:切换菜单、缩放、锁、画笔等),默认隐藏 |
settingsMenuVisible | boolean | 是否显示器材属性设置菜单,默认隐藏 |
saveButtonVisible | boolean | 是否显示保存按钮,默认隐藏 |
playerToolBarVisible | boolean | 是否显示预览模式工具条,默认隐藏 |
infoVisible | boolean | 是否显示器材信息,默认隐藏 |
equipmentLineColumn | boolean | 器材库是否显示一列,默认否(展示两列) |
functionBarLocationVisible | boolean | 是否显示功能栏位置,默认隐藏 |
loadingVisible | number|boolean | 是否显示loading,默认为 1。值说明:0 或 false, 不显示loading;1 或 true, 显示loading, 且自动隐藏,(实验加载完成后自动隐藏loading, 默认为此值); 2: 显示loading, 且不自动隐藏 |
preventDownload | boolean | 是否禁止下载,默认不禁止(影响截图、电路图、表格、dis、富文本等下载功能) |
videoFloatWinOnMobile | boolean | (仅移动端有效) 是否以浮窗形式显示移动端视频播放器,默认为false (即全屏显示)。 |
videoFullScreenBtnHidden | boolean | 是否隐藏视频播放器的全屏按钮,默认为false (即不隐藏)。 |
videoHideOnSetData | boolean | 调用setData 方法时,是否隐藏“边做边看”视频按钮,默认为true (即隐藏)。 |
introEditorRichReadOnMobile | number | (仅移动端有效) 设置移动端简介编辑器的模式。默认为0 (简单文本预览)。值为1 时为富文本预览(只读),2 时为富文本编辑(可编辑)。 |
introEditorFontSizeOnMobile | number | 当introEditorRichReadOnMobile 为1 或2 时生效,设置简介的默认字体大小为 22px。 |
css | object | 设置播放器css样式,样式修改请参阅UI组件与样式配置指南 |
代码示例
- 注意:
communication
是 调用步骤 中创建的通信对象
// 配置预览模式
await communication.get('config', {
leftToolbarVisible: true,
settingsMenuVisible: true,
playerToolBarVisible: true,
preventDownload: true,
});
// 配置编辑模式(展示器材库,可添加器材)
await communication.get('config', {
topToolbarVisible: true,
leftToolbarVisible: true,
rightToolbarVisible: true,
bottomToolbarVisible: true,
settingsMenuVisible: true,
saveButtonVisible: true,
equipmentLineColumn: true,
preventDownload: true,
});
getData(): string
获取实验场景数据
返回数据
- 返回实验场景数据的JSON字符串
代码示例
- 注意:
communication
是 调用步骤 中创建的通信对象
// 返回的数据是JSON字符串,可以将result持久化到数据库中
const result = await communication.get('getData');
// 写入数据库方法,WRITE_TO_DB 需要自行实现
WRITE_TO_DB(result);
setData(data: string): void
设置实验场景数据
参数说明
参数 | 类型 | 参数描述 |
---|---|---|
data | string | 实验的场景JSON字符串数据 |
代码示例
- 注意:
communication
是 调用步骤 中创建的通信对象
// 从数据库中读取场景数据,READ_FROM_DB 方法需要自行实现
const sceneData: string = await READ_FROM_DB();
await communication.get('setData', sceneData);
switchModule(id:number): void
清空实验场景
参数说明
参数 | 类型 | 参数描述 |
---|---|---|
id | number | 切换到指定模块的id |
模块id说明
id | 描述 |
---|---|
物理 | |
1 | 电与磁 |
2 | 家庭电路 |
3 | 声学 |
4 | 光学 |
5 | 热学 |
6 | 力学 |
7 | 力与运动 |
8 | 近代物理 |
化学 | |
9 | 无机化学 |
10 | 有机化学 |
27 | 电化学 |
代码示例
- 注意:
communication
是 调用步骤 中创建的通信对象
await communication.get('switchModule', 2);
clear(): void
清空实验场景
代码示例
- 注意:
communication
是 调用步骤 中创建的通信对象
await communication.get('clear');
play(): void
继续实验渲染
代码示例
- 注意:
communication
是 调用步骤 中创建的通信对象
await communication.get('play');
stop(): void
停止实验渲染
代码示例
- 注意:
communication
是 调用步骤 中创建的通信对象
await communication.get('stop');
needsSceneSave(): boolean
判断是否需要保存实验;使用场景为:关闭或退出实验时,判断是否需要保存,是则执行保存,否则直接关闭或退出
代码示例
- 注意:
communication
是 调用步骤 中创建的通信对象
const needsSceneSave = await communication.get('needsSceneSave');
if (needsSceneSave) {
// 保存实验方法 SAVE_EXPERIMENT 需要自行实现
SAVE_EXPERIMENT();
} else {
// to do quit or close
}
setSceneSave(): void
设置实验场景已保存;使用场景为:保存实验成功后,通知内部已执行成功保存操作
代码示例
- 注意:
communication
是 调用步骤 中创建的通信对象
await communication.get('setSceneSave');
setTitle(title: string): void
设置标题
代码示例
- 注意:
communication
是 调用步骤 中创建的通信对象
await communication.get('setTitle');
takeScreenShot(param: Object): string
屏幕截图,可以获取当前实验中的图像。
param参数说明
参数 | 类型 | 默认值 | 参数描述 |
---|---|---|---|
x | number | 0 | 相对于实验canvas中(0, 0)点的x坐标值 |
y | number | 0 | 相对于实验canvas中(0, 0)点的y坐标值 |
width | number | 场景宽度 | 需要截图的宽度 |
height | number | 场景高度 | 需要截图的高度 |
outWidth | number | 400 | 输出图片的宽度 |
outHeight | number | 根据outWidth 和场景宽高进行等比例计算 | 输出图片的高度 如果输出图像的宽高比和截取图像的宽高比不一致,输出的图像会按照 width 与height 等比缩放居中显示,jpg图片背景会填充背景色,png图片背景会用透明像素填充。 |
quality | number | 0.8 | 输出图片质量(仅对输出格式为jpg的图片有效) |
type | string | jpg | 输出图片的格式,支持类型:png ,jpg |
transparent | boolean | false | 是否输出背景透明的图像 |
返回数据
- 返回base64的图片格式的字符串
- 如果
type
设置的值是jpg,那么返回的数据如下
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAA......
- 如果
type
设置的值是png,那么返回的数据如下
data:image/png;base64,iVBORw0KGgoAAAANS......
- 如果
代码示例 (亲自试一试)
- 注意:
communication
是 调用步骤 中创建的通信对象
const result = await communication.get('takeScreenShot', {
x: 260, y: 80,
width: 640, height: 480,
outWidth: 320,
outHeight: 240,
quality: 0.8,
type: 'jpg',
});
- 调用方法传入的参数和最终的结果输出请参考下图
URL拼接参数
在获取实验链接时,可以在URL末尾添加参数来控制某些特定的行为。
参数详情
参数名称 | 类型 | 描述 |
---|---|---|
noNBSetDataOfURL | boolean | 默认值为false 。当设置为true 时,将禁用NOBOOK通过URL获取实验ID的功能,并忽略URL中包含的实验ID。 |
事件
onload
onload
事件在实验场景加载完成时触发。
onSave
onSave
事件会在用户点击保存按钮时触发,如果想要接收到此事件,需要通过config方法将topToolbarVisible
设置为true来显示顶部工具栏。
收到此事件后,可以再调用communication.get('getData')
方法来获取当前实验的场景数据。
部分场景示例代码
打开精品资源
- 注意:
communication
是 调用步骤 中创建的通信对象
// 监听实验场景加载完成时
communication.on('onload', async (data: {prod: string}) => {
console.log(`%c ~~~ onload 成功: ${JSON.stringify(data)}`, "color:red;");
// 预览模式
// 需自行封装SHOW_PLAYER,请查看API中config()
await SHOW_PLAYER();
// 编辑模式
// 需自行封装SHOW_EDITOR,请查看API中config()
await SHOW_EDITOR();
// 外部隐藏loading方法 HIDE_LOADING 需要自行实现
HIDE_LOADING();
});
打开我的实验
- 注意:
communication
是 调用步骤 中创建的通信对象
// 监听实验场景加载完成时
communication.on('onload', async (data: {prod: string}) => {
console.log(`%c ~~~ onload 成功: ${JSON.stringify(data)}`, "color:red;");
// 从数据库中读取场景数据,READ_FROM_DB 方法需要自行实现
// 如需优化加载速率,可自行实现异步读取场景数据,然后设置场景数据
const sceneData: string = await READ_FROM_DB();
await communication.get('setData', sceneData);
// SHOW_PLAYER or SHOW_EDITOR
// 外部隐藏loading方法 HIDE_LOADING 需要自行实现
HIDE_LOADING();
});
新建实验
// 监听实验场景加载完成时
communication.on('onload', async (data: {prod: string}) => {
console.log(`%c ~~~ onload 成功: ${JSON.stringify(data)}`, "color:red;");
// MODULE_ID为新建模块的id
await communication.get('switchModule', MODULE_ID)
// SHOW_EDITOR 新建实验为编辑模式专属
await SHOW_EDITOR()
// 外部隐藏loading方法,HIDE_LOADING 需要自行实现
HIDE_LOADING();
});
保存实验
- 注意:
communication
是 调用步骤 中创建的通信对象
// 获取实验场景数据
const sceneData = await communication.get('getData');
// 获取当前场景截图
const thumb = await communication.get('takeScreenShot', {
x: 0,
y: 0,
outWidth: 180,
outHeight: 100,
quality: 1,
type: "png",
});
// WRITE_TO_DB
const res = await WRITE_TO_DB({
sceneData,
thumb
});
if (res.code === 200) {
// 保存后通知内部当前场景保存成功
await communication.get('setSceneSave');
}
编辑模式退出实验
- 注意:
communication
是 调用步骤 中创建的通信对象
// 判断实验场景是否需要保存
const needsSceneSave = await communication.get('needsSceneSave');
if (needsSceneSave) {
// 保存实验方法 SAVE_EXPERIMENT 需要自行实现
const res = await SAVE_EXPERIMENT();
if(res.code !== 200) {
return;
}
}
// 退出 QUIT
QUIT();