跳到主要内容
版本:2.0

实验API调用

提示

物理化学学科的虚拟实验对接提供了一系列API接口,以便于您实现更多高级功能。

通常,虚拟实验对接会通过网页中的iframe元素进行嵌入。尽管iframe元素支持使用postMessage进行跨源通信,这种方式在需要双向数据传输时可能会变得复杂。具体来说,外部页面可以通过postMessageiframe内部发送信息,但iframe内部还需要回传数据给外部页面,这同样需要使用postMessage

为简化这一流程,我们参考了PostMate开源项目,并据此设计了一个简洁而高效的通信接口。

调用步骤

1. 安装前端SDK

npm 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

设置实验配置参数

参数说明

参数类型参数描述
topToolbarVisibleboolean是否显示顶部工具栏,默认隐藏
titleVisibleboolean是否显示顶部标题,默认隐藏
leftToolbarVisibleboolean是否显示左侧工具栏(如:电学转电路图、力学绘制),默认隐藏
rightToolbarVisibleboolean是否显示右侧工具栏(如:器材库),默认隐藏
bottomToolbarVisibleboolean是否显示底部工具栏(如:切换菜单、缩放、锁、画笔等),默认隐藏
settingsMenuVisibleboolean是否显示器材属性设置菜单,默认隐藏
saveButtonVisibleboolean是否显示保存按钮,默认隐藏
playerToolBarVisibleboolean是否显示预览模式工具条,默认隐藏
infoVisibleboolean是否显示器材信息,默认隐藏
equipmentLineColumnboolean器材库是否显示一列,默认否(展示两列)
functionBarLocationVisibleboolean是否显示功能栏位置,默认隐藏
loadingVisiblenumber|boolean是否显示loading,默认为 1。值说明:0 或 false, 不显示loading;1 或 true, 显示loading, 且自动隐藏,(实验加载完成后自动隐藏loading, 默认为此值); 2: 显示loading, 且不自动隐藏
preventDownloadboolean是否禁止下载,默认不禁止(影响截图、电路图、表格、dis、富文本等下载功能)
videoFloatWinOnMobileboolean(仅移动端有效) 是否以浮窗形式显示移动端视频播放器,默认为false(即全屏显示)。
videoFullScreenBtnHiddenboolean是否隐藏视频播放器的全屏按钮,默认为false(即不隐藏)。
videoHideOnSetDataboolean调用setData方法时,是否隐藏“边做边看”视频按钮,默认为true(即隐藏)。
introEditorRichReadOnMobilenumber(仅移动端有效) 设置移动端简介编辑器的模式。默认为0(简单文本预览)。值为1时为富文本预览(只读),2 时为富文本编辑(可编辑)。
introEditorFontSizeOnMobilenumberintroEditorRichReadOnMobile12时生效,设置简介的默认字体大小为 22px。
cssobject设置播放器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

设置实验场景数据

参数说明

参数类型参数描述
datastring实验的场景JSON字符串数据

代码示例

  • 注意: communication调用步骤 中创建的通信对象
// 从数据库中读取场景数据,READ_FROM_DB 方法需要自行实现
const sceneData: string = await READ_FROM_DB();
await communication.get('setData', sceneData);

switchModule(id:number): void

清空实验场景

参数说明

参数类型参数描述
idnumber切换到指定模块的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参数说明

参数类型默认值参数描述
xnumber0相对于实验canvas中(0, 0)点的x坐标值
ynumber0相对于实验canvas中(0, 0)点的y坐标值
widthnumber场景宽度需要截图的宽度
heightnumber场景高度需要截图的高度
outWidthnumber400输出图片的宽度
outHeightnumber根据outWidth和场景
宽高进行等比例计算
输出图片的高度
如果输出图像的宽高比和截取图像的宽高比不一致,输出的图像会按照widthheight等比缩放居中显示,jpg图片背景会填充背景色,png图片背景会用透明像素填充。
qualitynumber0.8输出图片质量(仅对输出格式为jpg的图片有效)
typestringjpg输出图片的格式,支持类型:png,jpg
transparentbooleanfalse是否输出背景透明的图像

返回数据

  • 返回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末尾添加参数来控制某些特定的行为。

参数详情

参数名称类型描述
noNBSetDataOfURLboolean默认值为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调用步骤 中创建的通信对象
  • 模块id: MODULE_ID 请参考 切换模块 中的模块id说明
// 监听实验场景加载完成时
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('getData');
// 获取当前场景截图
const thumb = await communication('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();