长期以来,Windows 一直支持数字笔,让用户以自然、直接的方式与设备交互,并通过丰富的写作和绘图体验使用数字墨迹表达他们的创造力。
在 Windows 11 中,引入了一项新功能,使数字笔体验更加自然且令人信服:使用支持“触觉反馈”的笔时,用户实际上可以通过触觉方式与应用的用户界面(UI)交互。
注释
引用此新功能时,“触觉”用于开发人员 API 和相关文档,而“触觉”是向用户提供的友好名称,用于在 Windows 设置中设置反馈首选项。
Windows 11 支持的触觉反馈体验包括 墨迹反馈 和 交互反馈:
- 在笔与屏幕接触时,墨迹书写反馈通过连续振动模拟各种书写或绘图工具(如钢笔、记号笔、铅笔、荧光笔等)的触感。 默认情况下,Windows Ink 平台 支持所有绘图工具的触觉反馈(本主题介绍如何提供超出 Windows Ink 支持的自定义墨迹书写解决方案)。
- 另一方面,交互反馈是基于关键用户操作的直接反馈,例如悬停或单击按钮、响应操作完成或引起用户的注意。
通常,需要执行五个步骤才能完全支持触觉反馈:
- 检测笔输入。
- 确定当前笔和设备是否支持触觉反馈,如果是,则支持哪些触觉反馈功能。
- 决定要发送的触觉反馈信号。
- 发送触觉反馈。
- 停止触觉反馈
检测笔输入
若要检测和隔离笔输入,必须先注册 PointerEntered 事件,然后检查 PointerDeviceType 是否为 笔。
以下代码演示如何检查 PointerEntered 事件中的指针设备类型。 对于此示例,如果输入不是来自笔,我们只是从事件处理程序返回。 否则,我们将检查笔功能并配置触觉反馈。
private void InputObserver_PointerEntered(object sender, PointerRoutedEventArgs e)
{
...
// If the current Pointer device is not a pen, exit.
if (e.Pointer.PointerDeviceType != PointerDeviceType.Pen)
{
return;
}
...
}
确定对触觉反馈的支持
并非所有笔和数字化器都支持触觉反馈,不一定支持本主题中所述的所有触觉反馈功能。 因此,请务必以编程方式确认活动笔支持哪些功能。
在前面的示例中的延续中,我们演示如何检查活动笔是否支持触觉反馈。
我们首先尝试从当前 PointerId中检索 PenDevice 对象。 如果无法获取 PenDevice,我们只需从事件处理程序返回。
如果已获取 PenDevice,我们将测试它是否支持 SimpleHapticsController 属性。 否则,我们就简单地从事件处理程序中返回。
// Attempt to retrieve the PenDevice from the current PointerId.
penDevice = PenDevice.GetFromPointerId(e.Pointer.PointerId);
// If a PenDevice cannot be retrieved based on the PointerId, it does not support
// advanced pen features, such as haptic feedback.
if (penDevice == null)
{
return;
}
// Check to see if the current PenDevice supports haptic feedback by seeing if it
// has a SimpleHapticsController.
hapticsController = penDevice.SimpleHapticsController;
if (hapticsController == null)
{
return;
}
在前面示例中检索到的 SimpleHapticsController 用于在后续示例中查询触觉能力和发送/停止触觉反馈。
注释
如果您正在使用 Windows 应用 SDK 预览版 1.0开发应用程序,则可以使用 PenDevice 互操作(PenDeviceInterop.FromPointerPoint(PointerPoint))来访问系统 PenDevice。
private void InputObserver_PointerEntered(PointerInputObserver sender, PointerEventArgs args)
{
var penDevice = PenDeviceInterop.PenDeviceFromPointerPoint(args.CurrentPoint);
}
以下部分介绍触觉笔必须支持的反馈功能,以及可选功能。 必需的触觉反馈类型通常可以作为备用选择,而不是可选功能。
墨水波形绘制
墨迹波形在笔与屏幕接触时连续生成,并尝试模拟不同书写或绘画工具的触感。
功能 / 特点 | DESCRIPTION | 必需/可选 |
---|---|---|
连续墨水波形 | 模拟用圆珠笔书写的感觉。 这是触觉笔不支持墨迹波形时的默认备选方案。 | 必选 |
连续刷波形 | 当用户选择画笔工具用于墨迹书写时,提供持续的触觉反馈。 | 可选 |
凿刻标记连续波形 | 当用户选择凿型标记/荧光笔作为书写工具时,会发出连续的触觉信号。 | 可选 |
连续橡皮擦波形 | 用户选择橡皮擦作为书写工具时,会有连续的触觉信号。 | 可选 |
GalaxyContinuous 波形 (HID 文档和实现指南将此波形称为 SparkleContinuous) |
特殊墨迹工具(如多色画笔)的持续触觉反馈信号。 | 可选 |
连续标记波形 | 当用户选择记号笔作为墨迹书写工具时,会提供连续的触觉信号。 | 可选 |
Pencil 连续波形 | 当用户选择铅笔作为书写工具时,系统会发出连续的触觉信号。 | 可选 |
交互波形
交互波形一般很短(下表中注明了例外情况),是按需生成的直接反馈波形,用于确认关键操作,例如悬停或点击按钮、响应操作的完成,或者吸引用户的注意力。
功能 / 特点 | DESCRIPTION | 必需/可选 |
---|---|---|
单击波形 | 简短的“单击”反馈。 当触觉笔不支持由应用程序选择的交互波形时,这是默认的备选项。 | 必选 |
错误波形 | 发出强信号,提醒用户操作失败或发生错误。 | 可选 |
悬停波形 | 指示用户已开始将鼠标悬停在交互式 UI 元素上。 | 可选 |
按下波形按钮 | 指示用户何时在增量操作中按下交互式 UI 元素(请参阅发布)。 | 可选 |
释放波形 | 指示用户何时在增量操作中释放交互式 UI 元素(请参阅 Press)。 | 可选 |
成功波形 | 强信号,提醒用户操作成功。 | 可选 |
BuzzContinuous 波形 | 连续的嗡嗡感觉。 | 可选 |
隆隆连续波形 | 连续隆隆的感觉。 | 可选 |
触觉反馈自定义
某些触觉笔可以支持以下自定义。
功能 / 特点 | DESCRIPTION | 必需/可选 |
---|---|---|
强度 | 设置触觉信号的强度。 | 可选 |
播放次数 | 重复指定次数的触觉信号。 | 可选 |
重播暂停间隔 | 设置每次重复播放触觉信号之间的时间。 | 可选 |
播放持续时间 | 设置播放触觉信号的时间间隔。 | 可选 |
检查是否支持自定义设置
若要检查对强度、播放计数、重播暂停间隔和播放持续时间的支持,请使用以下 SimpleHapticsController属性:
发送和停止触觉反馈墨迹书写
使用 SimpleHapticsController 对象的 SendHapticFeedback 方法将墨迹书写波形传递给用户的笔。 此方法支持传入波形或同时传递具有自定义强度值的波形(请参阅 自定义触觉反馈)。
调用 SendHapticFeedback 函数并传入一个 墨迹波形,以便在笔尖接触到屏幕任意位置后立即开始播放此波形。 波形将继续播放,直到提起笔或调用 StopFeedback,以先发生者为准。 建议在 PointerEntered 事件处理程序中执行此操作,以在您希望触觉反馈播放的元素中进行设置。 例如,具有自定义墨迹书写实现的应用将在墨迹书写画布的 PointerEntered 方法中执行此操作。
若要检索所需的 墨迹书写波形,您必须遍历 SimpleHapticsController的 SupportedFeedback 集合,并确保活动笔支持该波形。
如果它不受支持,可以选择不播放任何内容,或回退到 InkContinuous 波形,因为这确保受支持。
在以下示例中,我们尝试发送 BrushContinuous 波形(如果不支持 BrushContinuous,则切换到 InkContinuous)。
SimpleHapticsControllerFeedback currentWaveform;
// Attempt to set the currentWaveform to BrushContinuous.
foreach (var waveform in hapticsController.SupportedFeedback)
{
if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.BrushContinuous)
{
currentWaveform = waveform;
}
}
// If currentWaveform is null, it was not in the SupportedFeedback collection, so instead set
// the waveform to InkContinuous.
if (currentWaveform == null)
{
foreach (var waveform in hapticsController.SupportedFeedback)
{
if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.InkContinuous)
{
currentWaveform = waveform;
}
}
}
// Send the currentWaveform
hapticsController.SendHapticFeedback(currentWaveform);
重要的是,当关联的指针退出注册用于触觉反馈的元素时,您还应停止触觉反馈。 否则,波形将继续尝试在活动笔上播放。
注释
某些笔在离开屏幕范围时可能会自行停止触觉反馈。 但是,并非所有笔都不需要执行此操作,因此应用程序应始终按此处所述显式停止触觉反馈。
若要停止元素的触觉反馈,请在同一个元素上注册 PointerExited 事件,该元素之前已注册了发送触觉信号的 PointerEntered 处理程序。 在该退出的事件处理程序中,调用 StopFeedback,如下所示。
hapticsController.StopFeedback();
发送和停止交互反馈
发送交互反馈与发送墨迹反馈很相似。
使用 SimpleHapticsController 对象的 SendHapticFeedback 方法将交互波形传递给用户的笔。 此方法支持传入波形或同时传递具有自定义强度值的波形(请参阅 自定义触觉反馈)。
调用 SendHapticFeedback 并传入 书写波形,以便根据应用中的某些交互立即开始播放该波形(而不是在笔尖触摸屏幕获取墨迹反馈时)。
使用任何非连续交互波形时,无需进行相应的 StopFeedback 调用。 你仍需要为连续交互波形调用 StopFeedback。
注释
播放墨迹书写波形时发送交互波形将暂时中断墨迹书写波形。 交互波形停止时,墨迹书写波形将恢复。
若要检索所需的 交互波形,必须遍历 SimpleHapticsController的 SupportedFeedback 集合,确保其得到活动笔的支持。
如果不支持,可以选择完全不播放任何内容或回退到 单击 波形,因为这能确保受支持。
在以下示例中,我们尝试发送 错误 波形(但如果不支持该错误波形,则回退到“Click”)。
SimpleHapticsControllerFeedback currentWaveform;
// Attempt to set the currentWaveform to BrushContinuous.
foreach (var waveform in hapticsController.SupportedFeedback)
{
if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.Error)
{
currentWaveform = waveform;
}
}
// If currentWaveform is null, it was not in the SupportedFeedback collection, so instead set
// the waveform to Click.
if (currentWaveform == null)
{
foreach (var waveform in hapticsController.SupportedFeedback)
{
if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.Click)
{
currentWaveform = waveform;
}
}
}
// Send the currentWaveform.
hapticsController.SendHapticFeedback(currentWaveform);
自定义触觉反馈
可通过三种方法自定义触觉反馈。 第一个由墨迹书写和交互反馈支持,而第二和第三个则仅受交互反馈支持。
调整反馈强度,以适应系统最大强度设置。 为此,您必须首先检查 SimpleHapticsController 是否支持强度设置,然后调用 SendHapticFeedback,并传入所需的
Intensity
值。if (hapticsController.IsIntensitySupported) { foreach (var waveform in hapticsController.SupportedFeedback) { if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.Click) { double intensity = 0.75; hapticsController.SendHapticFeedback(waveform, intensity); } } }
重复触觉信号的指定次数。 为了做到这一点,您必须首先检查 SimpleHapticsController 是否支持设置强度,然后使用所需的计数值调用 SendHapticFeedbackForPlayCount。 还可以设置强度和重播暂停间隔。
注释
如果 SimpleHapticsController 不支持设置强度或重播暂停间隔,则提供的值将被忽略。
if (hapticsController.IsPlayCountSupported && hapticsController.IsIntensitySupported && hapticsController.IsReplayPauseIntervalSupported) { foreach (var waveform in hapticsController.SupportedFeedback) { if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.Click) { double intensity = 0.75; int playCount = 3; System.TimeSpan pauseDuration = new System.TimeSpan(1000000); hapticsController.SendHapticFeedbackForPlayCount(currentWaveform, intensity, playCount, pauseDuration); } } }
设置触觉信号的持续时间。 为此,必须先检查以确保 SimpleHapticsController 支持设置播放持续时间,然后使用所需的时间间隔值调用 SendHapticFeedbackForDuration。 还可以设置强度。
注释
如果 SimpleHapticsController 不支持设置强度,则提供的值将被忽略。
if (hapticsController.IsPlayDurationSupported && hapticsController.IsIntensitySupported) { foreach (var waveform in hapticsController.SupportedFeedback) { if (waveform.Waveform == KnownSimpleHapticsControllerWaveforms.RumbleContinuous) { double intensity = 0.75; System.TimeSpan playDuration = new System.TimeSpan(5000000); hapticsController.SendHapticFeedbackForDuration(currentWaveform, intensity, playDuration); } } }
例子
有关以下功能的工作示例,请参阅 笔触觉示例:
- 从笔输入获取 SimpleHapticsController:依次从 PointerId 转到 PenDevice,再到 SimpleHapticsController(需要具有触觉功能的笔和支持该笔的设备)。
- 检查笔触觉功能:SimpleHapticsController 公开与笔硬件功能相关的属性,包括 IsIntensitySupported、IsPlayCountSupported、SupportedFeedback等。
- 启动和停止触觉反馈:适当使用 SendHapticFeedback 和 StopFeedback 方法。
- 触发触觉反馈:墨迹书写反馈 和 交互反馈。