Реализуйте хаптильную обратную связь в приложениях Windows

В этой статье объясняется, как добавить тактильную отдачу в ваше приложение Windows с помощью API InputHapticsManager, а также приводятся примеры кода для типовых сценариев взаимодействия.

Чтобы узнать, когда и почему следует использовать тактильную отдачу, см. Рекомендации по проектированию тактильной отдачи.

Поддерживаемые устройства

Хаптики поддерживаются на устройствах ввода, таких как мыши, сенсорные панели и ручки. Доступность зависит от модели и производителя. Всегда проверяйте поддержку во время выполнения перед вызовом api-интерфейсов haptics (см. сведения о поддержке ниже).

Requirements

Requirement Сведения
Namespace Windows.Devices.Haptics
Минимальная ОС Windows 11, пакет SDK 10.0.28000.1721 (март 2026 г.)
Выравнивание / Столкновение / Шаг / Увеличение формы волны ПАКЕТ SDK 10.0.28000.1839 (апрель 2026 г.)
Контракт API UniversalApiContract версия 19.0

Чтобы использовать более ранние версии ОС, используйте ApiInformation.IsTypePresent для защиты всех вызовов (см. приведенный ниже код).

Проверка поддержки

Всегда проверяйте, что API доступен и что устройство тактильной отдачи подключено, прежде чем использовать его. API недоступен в более старых версиях ОС, и не все устройства ввода поддерживают тактильную отдачу.

using Windows.Devices.Haptics;
using Windows.Foundation.Metadata;

// Guard against older OS versions where the API is absent.
bool apiPresent = ApiInformation.IsTypePresent("Windows.Devices.Haptics.InputHapticsManager");

// Check that a supported haptic device is connected.
bool supported = apiPresent && InputHapticsManager.IsSupported();

Активация формы волны

Вызовите InputHapticsManager.GetForCurrentThread() в потоке пользовательского интерфейса, чтобы получить экземпляр диспетчера, связанный с фокусом ввода текущего потока. Обнаружение и маршрутизация устройств обрабатываются автоматически. Вам не нужно самостоятельно перечислять устройства.

using Windows.Devices.Haptics;

if (supported)
{
    var mgr = InputHapticsManager.GetForCurrentThread();

    // The second parameter is a fallback waveform used if the first is
    // unsupported. Passing 0 lets the system choose a device-appropriate
    // fallback. TrySendHapticWaveform returns false if neither waveform is
    // supported; this is a non-fatal condition.
    bool sent = mgr.TrySendHapticWaveform(
        KnownSimpleHapticsControllerWaveforms.Align,
        0);
}

Это важно

Вызывайте GetForCurrentThread() из потока пользовательского интерфейса (или потока, которому принадлежит фокус ввода). При вызове из фонового потока обратная связь не будет направляться на правильное устройство.

Остановка воспроизведения

Вызовите TryStopFeedback по завершении перетаскивания или взаимодействия, чтобы остановить любое текущее воспроизведение тактильной отдачи.

using Windows.Devices.Haptics;

if (supported)
{
    InputHapticsManager
        .GetForCurrentThread()
        .TryStopFeedback();
}

Примеры реализации

Выравнивание объектов

Руководства по выравниванию часто используются в средствах разработки, редакторах презентаций и приложениях для схем. Тактильная отдача добавляет второй канал восприятия, так что пользователи могут ощутить, когда объекты встают точно на место, уменьшая необходимость увеличивать масштаб, чтобы визуально подтвердить выравнивание.

Когда следует стрелять: Используйте внешний вид руководства по выравниванию в качестве триггера. Когда логика привязки показывает направляющую, включайте тактильный отклик. Если руководство не отображается, не стреляйте. Это обеспечивает тесную связь тактильных событий с видимой реакцией системы.

Избегайте повторов: Фигура может одновременно выравниваться по нескольким направляющим (например, по левому и верхнему краю). Считайте это одним взаимодействием: отслеживайте активные подсказки и срабатывайте только при появлении новой подсказки.

using Windows.Devices.Haptics;

private readonly HashSet<int> _activeGuides = new();

// Call this after computing which alignment guides are currently visible.
void OnGuidesUpdated(IEnumerable<int> currentGuideIds)
{
    var currentGuides = currentGuideIds.ToHashSet();

    bool hasNewGuide = currentGuides.Except(_activeGuides).Any();

    // Update the tracked set in place (field is readonly).
    _activeGuides.Clear();
    _activeGuides.UnionWith(currentGuides);

    if (hasNewGuide && supported)
    {
        InputHapticsManager.GetForCurrentThread()
            .TrySendHapticWaveform(
                KnownSimpleHapticsControllerWaveforms.Align,
                0);
    }
}

Sliders

Haptics улучшает взаимодействие ползунка путем привязки обратной связи к значимым позициям. Всегда связывайте обратную связь с видимыми маркерами, которые пользователь может понять— галочки, именованные значения или границы диапазона.

Когда запускать: Запускайте тактильный отклик, когда большой палец пересекает каждое деление. Избегайте непрерывной обратной связи между тиками, так как это может отвлекать.

using Windows.Devices.Haptics;

double _previousValue;
const double TickInterval = 25.0;

void OnSliderValueChanged(double newValue)
{
    int previousTick = (int)(_previousValue / TickInterval);
    int currentTick  = (int)(newValue       / TickInterval);

    if (currentTick != previousTick && supported)
    {
        InputHapticsManager.GetForCurrentThread()
            .TrySendHapticWaveform(
                KnownSimpleHapticsControllerWaveforms.Step,
                0);
    }

    _previousValue = newValue;
}

Улучшение взаимодействия: Регулируйте интенсивность тактильной отдачи в зависимости от положения ползунка, чтобы помочь пользователям понимать, где именно они находятся в пределах диапазона. Используйте TrySendHapticWaveformForPlayCount, который принимает явное значение интенсивности от 0.0 до 1.0.

void OnSliderValueChangedWithIntensity(double newValue, double maxValue)
{
    int previousTick = (int)(_previousValue / TickInterval);
    int currentTick  = (int)(newValue       / TickInterval);

    if (currentTick != previousTick && supported)
    {
        double intensity = newValue / maxValue; // Scale 0.0–1.0

        InputHapticsManager.GetForCurrentThread()
            .TrySendHapticWaveformForPlayCount(
                KnownSimpleHapticsControllerWaveforms.Step,
                0,          // fallback waveform
                intensity,
                1,          // playCount
                TimeSpan.Zero);
    }

    _previousValue = newValue;
}

Взаимодействие перетаскиванием и определение намерения

Во время перетаскивания объект может пересекать множество границ выравнивания — центр страницы, направляющие, другие объекты. Срабатывание тактильной отдачи при каждом пересечении может перегружать пользователей, особенно во время быстрых движений, когда они просто меняют положение, а не стремятся к точности.

Используйте обнаружение намерения, чтобы запускать отклик только тогда, когда пользователь целенаправленно выполняет выравнивание, а не просто проходит мимо.

Подход 1. Фильтрация скорости

Отключайте тактильную отдачу при быстром перемещении курсора, оставляя её для более медленных и осознанных движений.

  • Стабилизировать сигнал. Избегайте использования необработанных дельт перемещения курсора, так как они могут давать шумные данные в зависимости от типа устройства ввода, настроек DPI и конфигурации дисплея. Нормализовать и сгладить сигнал.
  • Тщательно выберите пороговое значение. Проверьте на разных устройствах, с разными типами ввода, масштабами отображения и в многодисплейных конфигурациях.

Подход 2. Таймер debounce

Когда объект входит в зону триггера, запустите короткий таймер (50 мс является разумной отправной точкой). Если объект по завершении отсчёта таймера остаётся в зоне, считайте это намеренным действием и включите тактильный отклик. Если объект перемещается до срабатывания таймера, отключите обратную связь.

  • Проще в реализации и, как правило, обеспечивает более стабильное поведение, чем фильтрация скорости.
  • Задержка в 50 мс достаточно длинна, чтобы отличить преднамеренное выравнивание от быстрого прохода, оставаясь ниже порогового значения заметной задержки.
using System;
using Microsoft.UI.Xaml;
using Windows.Devices.Haptics;

private DispatcherTimer? _alignTimer;

void OnObjectEnteredAlignmentZone()
{
    _alignTimer?.Stop();
    _alignTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(50) };
    _alignTimer.Tick += (_, _) =>
    {
        _alignTimer.Stop();
        if (supported)
        {
            InputHapticsManager.GetForCurrentThread()
                .TrySendHapticWaveform(
                    KnownSimpleHapticsControllerWaveforms.Align,
                    0);
        }
    };
    _alignTimer.Start();
}

void OnObjectLeftAlignmentZone()
{
    _alignTimer?.Stop();
}

Выбор между двумя

Фильтр скорости Таймер подавления дребезга
Принцип работы Реагирует — отслеживает движение в режиме реального времени Прогнозный — ожидает краткого подтверждения намерения
Чувствовать Может ощущаться более мгновенным Немного отложено, но более согласованно
Настройка усилий Более высокий — требует тщательной калибровки Ниже — обычно достаточно одного значения задержки.
Рекомендуется для Приложения, требующие высокой скорости отклика Большинство приложений; хорошая начальная точка по умолчанию