参考
tact-js 包提供了从 JavaScript 控制 bHaptics 触觉设备的函数。它使您能够播放触觉模式、直接控制设备马达,以及管理触觉播放。
import Tact from "tact-js";
基于事件的触觉播放
播放绑定到特定事件的触觉模式(在 bHaptics Designer/Portal 中设计)。对于大多数应用程序,这些是推荐使用的函数。
play
play({ eventKey, startTime, intensityRatio, durationRatio, offsetX, offsetY, deviceIndex }: PlayParams): Promise<void>;
type PlayParams = {
eventKey: string; // Name of the haptic event
startTime?: number; // Playback start position in milliseconds
intensityRatio?: number; // Intensity multiplier [0.0 - 2.0]
durationRatio?: number; // Duration multiplier
offsetX?: number; // Rotation offset [0.0 - 360.0]
offsetY?: number; // Vertical offset [-0.5 - 0.5]
deviceIndex?: number; // Target device index
};
播放预定义的触觉事件。
参数
eventKey:触觉事件的名称。startTime(可选):播放起始位置(以毫秒为单位)——事件的前startTime毫秒会被跳过。例如,以startTime: 2000播放一个 3,260ms 的事件时,只会播放最后的 1,260ms。(默认值:0)intensityRatio(可选):触觉 Intensity 倍数。有效范围:[0.0-2.0](默认值:1.0)durationRatio(可选):Duration 倍数。(默认值:1.0)offsetX(可选):将触觉逆时针旋转。有效范围:[0.0-360.0](默认值:0.0)offsetY(可选):将触觉上移或下移。有效范围:[-0.5-0.5](默认值:0.0)deviceIndex(可选):当连接了多个相同位置类型的设备时,作为目标的设备索引。(默认值:-1,不指定特定设备)
返回值
请求发送到 bHaptics Player 后,promise 会以无返回值(undefined)的形式 resolve。
请注意,即使事件键未注册,它也会正常 resolve——在这种情况下不会播放任何内容,也不会抛出错误。请使用 getEvent 检查事件键是否存在。
示例
import Tact from "tact-js";
const playExample = async () => {
await Tact.play({ eventKey: "heartbeat" });
};
const playMoreExample = async () => {
await Tact.play({
eventKey: "hit",
startTime: 0,
intensityRatio: 1.0,
durationRatio: 1.0,
offsetX: 180.0,
offsetY: 0.25,
});
};
playLoop
playLoop({ eventKey, intensityRatio, durationRatio, interval, maxCount, offsetX, offsetY, deviceIndex }: PlayLoopParams): Promise<number>;
type PlayLoopParams = {
eventKey: string; // Name of the haptic event
intensityRatio?: number; // Intensity multiplier [0.0 - 2.0]
durationRatio?: number; // Duration multiplier
interval: number; // Pause between repetitions (milliseconds)
maxCount: number; // Total number of plays
offsetX: number; // Rotation offset [0.0 - 360.0]
offsetY: number; // Vertical offset [-0.5 - 0.5]
deviceIndex?: number; // Target device index
};
将触觉事件循环播放指定的次数。
参数
eventKey:触觉事件的名称。intensityRatio(可选):触觉 Intensity 倍数。有效范围:[0.0-2.0](默认值:1.0)durationRatio(可选):Duration 倍数。(默认值:1.0)interval:重复之间的暂停时间(以毫秒为单位),从一次播放结束到下一次播放开始进行测量。例如,以interval: 500和maxCount: 3播放一个 460ms 的事件,会在约 2,380ms(460 + 500 + 460 + 500 + 460)后结束。maxCount:播放触觉事件的总次数(包括第一次播放)。小于1的值会按1处理。offsetX:将触觉逆时针旋转。有效范围:[0.0-360.0](默认值:0.0)offsetY:将触觉上移或下移。有效范围:[-0.5-0.5](默认值:0.0)deviceIndex(可选):当连接了多个相同位置类型的设备时,作为目标的设备索引。(默认值:-1,不指定特定设备)
返回值
标识此播放请求的请求 ID。
请注意,即使事件键未注册,也会返回请求 ID——在这种情况下不会播放任何内容。要停止循环播放的事件,请使用带有事件键的 stop 或 stopAll。
在循环运行期间,isPlayingByEventKey 在包括重复之间的 interval 暂停在内的整个区间内都返回 true。
示例
import Tact from "tact-js";
const playLoopExample = async () => {
const requestId = await Tact.playLoop({
eventKey: "hit",
intensityRatio: 1.0,
durationRatio: 1.0,
interval: 1000,
maxCount: 5,
offsetX: 0.0,
offsetY: 0.0,
});
console.log(`Playback request ID: ${requestId}`);
};
直接播放触觉
如果您想在不使用事件的情况下播放触觉,请使用这些函数。
playDot
playDot({ position, motorValues, duration, deviceIndex }: PlayDotParams): Promise<number>;
type PlayDotParams = {
position: PositionType; // Device position type
motorValues: number[]; // Array of motor intensity values [0 - 100]
duration?: number; // Duration in milliseconds
deviceIndex?: number; // Target device index
};
在特定的触觉执行器上播放触觉反馈。
参数
position:要播放的触觉设备类型。请参考PositionTypeenum。motorValues:每个马达的 Intensity 值数组。数组长度必须与设备中的马达数量一致。每个值必须为整数且在范围内。有效范围:[0-100]。duration(可选):触觉反馈的 Duration(以毫秒为单位)。建议大于或等于100。deviceIndex(可选):当连接了多个相同位置类型的设备时,作为目标的设备索引。(默认值:-1,不指定特定设备)
返回值
标识此播放请求的请求 ID。要停止播放,请使用 stopAll。
请注意,参数不会在客户端进行验证——例如,即使 motorValues 的长度不正确或包含超出范围的值,也会返回请求 ID;在这种情况下,触觉可能根本不会播放。
示例
import Tact, { PositionType } from "tact-js";
const playDotExample = async () => {
const requestId = await Tact.playDot({
position: PositionType.ForearmL, // Left TactSleeve
motorValues: [100, 80, 60], // TactSleeve has 3 motors
duration: 100
});
console.log(`Playback request ID: ${requestId}`);
}
playPath
playPath({ position, x, y, intensity, duration, deviceIndex }: PlayPathParams): Promise<number>;
type PlayPathParams = {
position: PositionType; // Device position type
x: number[]; // Array of x coordinates [0.0 - 1.0]
y: number[]; // Array of y coordinates [0.0 - 1.0]
intensity: number[]; // Array of intensity values [0 - 100]
duration?: number; // Duration in milliseconds
deviceIndex?: number; // Target device index
};
沿指定的坐标播放触觉反馈。与控制单个马达的 playDot 不同,此方法为设备上的特定坐标指定触觉 Intensity。
在指定触觉位置时,playDot 提供离散控制,而 playPath 则更为连续。playDot 为单个执行器分配 Intensity,而 playPath 允许为特定坐标(X 轴和 Y 轴均在 0 到 1 之间)指定 Intensity,从而使附近的执行器相应地振动。
您可以指定多个坐标和多个 Intensity。请注意,列表中这些坐标周围的所有执行器都会同时激活,而不是依次激活。所有数组(x、y 和 intensity)的大小必须相同。
如果 x、y 和 intensity 数组的长度不同,SDK 内部的 WebAssembly 模块会因不可恢复的错误而崩溃(已在 tact-js 2.0.4 中确认)——在重新加载页面之前,触觉将无法工作。在调用之前,请务必确保这三个数组的长度相同。
通过在逐渐改变值的同时持续调用此函数,您可以实现触觉点移动的效果。

参数
position:要播放的触觉设备类型。请参考PositionTypeenum。x:触觉效果的 X 坐标数组。每个值必须在范围内:[0.0-1.0]y:触觉效果的 Y 坐标数组。每个值必须在范围内:[0.0-1.0]intensity:每个坐标的 Intensity 值数组。每个值必须为整数且在范围内:[0-100]duration(可选):触觉反馈的 Duration(以毫秒为单位)。建议大于或等于100。deviceIndex(可选):当连接了多个相同位置类型的设备时,作为目标的设备索引。(默认值:-1,不指定特定设备)
返回值
标识此播放请求的请求 ID。要停止播放,请使用 stopAll。
示例
import Tact, { PositionType } from "tact-js";
const HAPTIC_PATHS = [
{
x: [0.738, 0.723, 0.709, 0.696, 0.682, 0.667, 0.653],
y: [0.680, 0.715, 0.749, 0.782, 0.816, 0.852, 0.885],
intensity: 20
},
{
x: [0.061, 0.072, 0.102, 0.184, 0.254, 0.310, 0.363],
y: [0.632, 0.587, 0.542, 0.498, 0.411, 0.366, 0.301],
intensity: 60
}
];
const TIME_OF_FRAME = 250; // milliseconds
const playPathExample = async () => {
const frames = HAPTIC_PATHS[0].x.length;
for (let i = 0; i < frames; i++) {
await Tact.playPath({
position: PositionType.Vest, // TactSuit Pro
x: HAPTIC_PATHS.map(point => point.x[i]),
y: HAPTIC_PATHS.map(point => point.y[i]),
intensity: HAPTIC_PATHS.map(point => point.intensity),
duration: TIME_OF_FRAME
});
await new Promise(resolve => setTimeout(resolve, TIME_OF_FRAME));
}
}
playGlove
playGlove({ position, motors, playtimes, shapes, repeatCount }: PlayGloveParams): Promise<number>;
type PlayGloveParams = {
position: PositionType; // GloveL or GloveR
motors: Int32Array; // Array of 8 motor intensity values [0 - 100]
playtimes: Int32Array; // Array of 8 time interval values
shapes: Int32Array; // Array of 8 waveform values
repeatCount: number; // Number of times to play the pattern
};
仅限 TactGlove。 在 TactGlove 上播放触觉,并对 Duration 和 Intensity 模式进行精细控制。与 playDot 相比,这提供了更详细的控制。
每个数组参数必须正好包含八个元素,对应手套中的每个马达。
参数
-
position:要播放的触觉设备类型。必须使用PositionType.GloveL或PositionType.GloveR。 -
motors:由 8 个 Intensity 值组成的数组,每个马达一个。每个值必须为整数且在范围内:[0-100]Array Index 马达所在位置… 0拇指尖 1食指尖 2中指尖 3无名指尖 4小指尖 5手腕处 6手掌(拇指侧) 7手掌(小指侧) -
playtimes:由 8 个时间间隔值组成的数组。有效值为:Value Duration 15ms 210ms 420ms 630ms 840ms -
shapes:由 8 个波形值组成的数组,用于控制 Intensity 随时间的变化方式:Value Waveform Pattern 0在该 Duration 内保持恒定 Intensity 1从指定的 Intensity 开始并减小一半 2从一半 Intensity 开始并增大至最大 -
repeatCount:播放 8 马达模式的总次数(包括第一次播放)。
返回值
标识此播放请求的请求 ID。要停止播放,请使用 stopAll。
示例
import Tact, { PositionType } from "tact-js";
const playGloveExample = async () => {
const motors = new Int32Array([100, 100, 100, 100, 100, 100, 100, 100]);
const playtimes = new Int32Array([8, 8, 8, 8, 8, 8, 8, 8]);
const shapes = new Int32Array([2, 2, 2, 2, 2, 2, 2, 2]);
const requestId = await Tact.playGlove({
position: PositionType.GloveR,
motors,
playtimes,
shapes,
repeatCount: 3
});
console.log(`Playback request ID: ${requestId}`);
}
播放控制
pause
pause(eventKey: string): Promise<void>
暂停当前正在播放的特定触觉事件。
参数
eventKey:要暂停的触觉事件的名称。
示例
import Tact from "tact-js";
const playAndPauseExample = async () => {
await Tact.play({ eventKey: "dash" });
await new Promise(resolve => setTimeout(resolve, 1000));
await Tact.pause("dash");
}
resume
resume(eventKey: string): Promise<void>
恢复之前暂停的触觉事件。
参数
eventKey:要恢复的触觉事件的名称。
示例
import Tact from "tact-js";
const playPauseAndResumeExample = async () => {
await Tact.play({ eventKey: "dash" });
await new Promise(resolve => setTimeout(resolve, 1000));
await Tact.pause("dash");
await new Promise(resolve => setTimeout(resolve, 1000));
await Tact.resume("dash");
}
stop
stop(eventKey: string): Promise<void>
完全停止特定的触觉事件。
参数
eventKey:要停止的触觉事件的名称。
示例
import Tact from "tact-js";
const stopExample = async () => {
await Tact.play({ eventKey: "dash" });
await new Promise(resolve => setTimeout(resolve, 1000));
await Tact.stop("dash");
}
stopAll
stopAll(): Promise<void>
停止当前正在播放的所有触觉。
示例
import Tact from "tact-js";
const stopAllExample = async () => {
await Tact.play({ eventKey: "dash" });
await Tact.play({ eventKey: "heartbeat" });
await Tact.play({ eventKey: "hit" });
await Tact.play({ eventKey: "slash" });
await new Promise(resolve => setTimeout(resolve, 500));
await Tact.stopAll();
}
生命周期
init
init({ appId, apiKey }: InitParams): Promise<boolean>;
type InitParams = {
appId: string; // Your bHaptics App ID
apiKey: string; // Your bHaptics API Key
};
初始化 bHaptics SDK。在使用 SDK 中的任何其他函数之前,必须调用此函数。它会初始化 SDK 并建立与 bHaptics Player 的连接。
参数
appId:来自 bHaptics Developer Portal 的 bHaptics 应用程序 IDapiKey:来自 bHaptics Developer Portal 的 bHaptics API 密钥
返回值
初始化成功时返回 true,否则返回 false。
示例
import Tact from "tact-js";
const initTact = async () => {
const result = await Tact.init({
appId: "your-app-id",
apiKey: "your-api-key",
});
console.log(result ? "Connection Success!" : "Connection Failed...");
}
状态检查
getConnectedDevices
getConnectedDevices(): Promise<any[]>
获取所有已连接触觉设备的列表。
返回值
已连接设备对象的数组。当没有设备连接时返回空数组([])。每个对象具有以下字段:
| Field | Type | Description |
|---|---|---|
position | number | 设备的数字位置:0 Vest、1 ForearmL、2 ForearmR、3 Head、4 HandL、5 HandR、6 FootL、7 FootR、8 GloveL、9 GloveR。使用 PositionUtils.positionToType(position) 可将其转换为 PositionType。 |
deviceName | string | 设备的显示名称(例如 "TactGlove (L)")。 |
address | string | 设备的 MAC 地址。与 ping 一起使用。 |
connected | boolean | 设备当前是否已连接。 |
paired | boolean | 设备是否已在 bHaptics Player 中配对。 |
battery | number | 电池电量(百分比)。 |
audioJackIn | boolean | 是否插入了音频插头(音频配件设备)。 |
vsm | number | 在 bHaptics Player 中配置的振动强度设置。 |
返回值的示例如下:
[
{
"position": 8,
"deviceName": "TactGlove (L)",
"address": "F418EB165E99",
"connected": true,
"paired": true,
"battery": 91,
"audioJackIn": false,
"vsm": 20
},
{
"position": 9,
"deviceName": "TactGlove (R)",
"address": "DF8B33412EC5",
"connected": true,
"paired": true,
"battery": 84,
"audioJackIn": false,
"vsm": 20
}
]
示例
import Tact from "tact-js";
const showDevicesExample = async () => {
const devices = await Tact.getConnectedDevices();
console.log("Connected devices:", devices);
}
getHapticMappings
getHapticMappings(): Promise<any[]>
获取当前触觉应用程序中注册的所有触觉事件(从 bHaptics Developer Portal 部署)的列表。
返回值
触觉映射对象的数组。每个对象具有以下字段:
| Field | Type | Description |
|---|---|---|
eventName | string | 事件键。与 play 及其他基于事件的函数一起使用。 |
eventTime | number | 事件的 Duration(以毫秒为单位)。 |
返回值的示例如下:
[
{
"eventName": "punch-recoil",
"eventTime": 180
},
{
"eventName": "dash",
"eventTime": 3260
},
{
"eventName": "teleport",
"eventTime": 460
}
]
示例
import Tact from "tact-js";
const showMappingsExample = async () => {
const mappings = await Tact.getHapticMappings();
for (const mapping of mappings) {
console.log(`${mapping.eventName}: ${mapping.eventTime}ms`);
}
}
getEvent
getEvent(eventKey: string): Promise<number>
获取已注册触觉事件的 Duration。
参数
eventKey:要查询的触觉事件的名称。
返回值
事件的 Duration(以毫秒为单位)。如果事件键未注册,则返回 0——您可以用它在调用 play 之前检查事件键是否存在。
示例
import Tact from "tact-js";
const getEventExample = async () => {
const duration = await Tact.getEvent("dash");
if (duration > 0) {
console.log(`dash is ${duration}ms long`);
} else {
console.log("dash is not registered");
}
}
isDeviceConnected
isDeviceConnected(position: PositionType): Promise<boolean>
检查特定设备类型是否已连接。
参数
position:要播放的触觉设备类型。请参考PositionTypeenum。
返回值
如果该设备类型已连接则返回 true,否则返回 false。
示例
import Tact, { PositionType } from "tact-js";
const isDeviceConnectedExample = async () => {
const isGloveConnected = await Tact.isDeviceConnected(PositionType.GloveR);
console.log("Right TactGlove connected:", isGloveConnected);
}
isConnected
isConnected(): Promise<boolean>
检查与 bHaptics Player 的连接状态。
返回值
如果已连接到 bHaptics Player 则返回 true,否则返回 false。
isPlaying
isPlaying(): Promise<boolean>
检查当前是否有任何触觉反馈正在播放。
返回值
如果有任何触觉正在播放则返回 true,否则返回 false。
isPlayingByEventKey
isPlayingByEventKey(eventKey: string): Promise<boolean>
检查特定触觉事件当前是否正在播放。
参数
eventKey:要检查的触觉事件的名称
返回值
如果指定的触觉事件正在播放则返回 true,否则返回 false。
设备控制
ping
ping(address: string): Promise<void>
向特定设备发送 ping 信号以测试连接。
参数
address:触觉设备的 MAC 地址。您可以通过getConnectedDevices()获取该地址
示例
import Tact from "tact-js";
const pingFirstDeviceExample = async () => {
const devices = await Tact.getConnectedDevices();
if (devices.length > 0) {
await Tact.ping(devices[0].address);
}
}
pingAll
pingAll(): Promise<void>
向所有已连接的设备发送 ping 信号。
motorTest
motorTest(): Promise<void>
在 TactSuit Pro(Vest)上运行马达测试序列:32 个马达位置中的每一个都会以最大 Intensity 依次触发一秒钟。整个序列大约需要 32 秒;返回的 promise 会在序列结束时 resolve。
请注意,此测试仅涵盖 Vest 位置——它不会测试其他设备类型。
通用 Enum
PositionType
enum PositionType {
Vest = "Vest",
ForearmL = "ForearmL",
ForearmR = "ForearmR",
Head = "Head",
HandL = "HandL",
HandR = "HandR",
FootL = "FootL",
FootR = "FootR",
GloveL = "GloveL",
GloveR = "GloveR"
}
字符串 enum。用它来确定触觉设备的类型。
| Value | Device | Motors Count |
|---|---|---|
Vest | TactSuit Pro | 32 |
ForearmL | TactSleeve(Left) | 3 |
ForearmR | TactSleeve(Right) | 3 |
Head | TactVisor | 4 |
HandL | Tactosy for Hands(Left) | 3 |
HandR | Tactosy for Hands(Right) | 3 |
FootL | Tactosy for Feet(Left) | 3 |
FootR | Tactosy for Feet(Right) | 3 |
GloveL | TactGlove(Left) | 8 |
GloveR | TactGlove(Right) | 8 |
tact-js 还导出了一个 PositionUtils 类,其中包含用于在 getConnectedDevices 使用的数字位置与 PositionType enum 之间进行转换的静态辅助方法:
import { PositionType, PositionUtils } from "tact-js";
PositionUtils.positionToType(9); // PositionType.GloveR
PositionUtils.enumToPosition(PositionType.Vest); // 0