鸿蒙震动技术实战

概述

在移动应用开发中,触觉反馈是提升用户体验的重要组成部分。适当的震动反馈可以增强用户与应用的交互感,提供操作确认,减轻视觉依赖,甚至可以传递情感信息。华为 HarmonyOS 提供了丰富的震动 API,使开发者能够为应用添加多样化的触觉反馈效果。

本文将详细介绍 HarmonyOS 中的震动 API,结合 CatIsland 项目的实际应用,展示如何在应用中实现各种震动效果,并提供一个可直接使用的震动工具类。

震动 API 介绍

核心模块

HarmonyOS 提供了 @kit.SensorServiceKit 模块用于控制设备震动,主要使用其中的 vibrator 相关 API。

震动类型

HarmonyOS 支持三种类型的震动:

  1. 时长震动:指定震动持续时间
  2. 预设效果:使用系统预设的震动模式
  3. 自定义模式:通过震动模式数组实现复杂效果

核心 API

1. 时长震动
import { vibrator } from "@kit.SensorServiceKit";
import { BusinessError } from "@kit.BasicServicesKit";

// 启动指定时长的震动
vibrator.startVibration({
  type: 'time',
  duration: 100 // 震动持续时间,单位毫秒
}, {
  usage: 'physicalFeedback' // 震动用途
}, (error: BusinessError<void>) => {
  if (error) {
    console.error(`启动震动失败: ${error.code}, ${error.message}`);
    return;
  }
  console.info('震动启动成功');
});
2. 预设效果震动
// 启动预设效果的震动
vibrator.startVibration({
  type: 'preset',
  effectId: 'haptic.effect.soft', // 预设效果ID
  count: 1 // 震动次数
}, {
  usage: 'physicalFeedback'
}, (error: BusinessError<void>) => {
  if (error) {
    console.error(`启动震动失败: ${error.code}, ${error.message}`);
    return;
  }
  console.info('震动启动成功');
});
3. 自定义模式震动
// 启动自定义模式的震动
vibrator.startVibration({
  type: 'pattern',
  pattern: [100, 200, 100, 300], // 震动模式数组,交替表示震动和暂停的时长
  repeat: -1 // 重复次数,-1表示无限循环
}, {
  usage: 'physicalFeedback'
}, (error: BusinessError<void>) => {
  if (error) {
    console.error(`启动震动失败: ${error.code}, ${error.message}`);
    return;
  }
  console.info('震动启动成功');
});
4. 停止震动
// 停止震动
vibrator.stopVibration('all', (error: BusinessError<void>) => {
  if (error) {
    console.error(`停止震动失败: ${error.code}, ${error.message}`);
    return;
  }
  console.info('震动停止成功');
});
5. 检查效果是否支持
// 检查预设效果是否支持
vibrator.isSupportEffect('haptic.effect.soft', (err: BusinessError<void>, state: boolean) => {
  if (err) {
    console.error(`检查效果失败: ${err.code}, ${err.message}`);
    return;
  }
  console.info(`效果是否支持: ${state}`);
  if (state) {
    // 效果支持,可以使用
  }
});

最佳实践

在实际项目开发中,震动反馈的合理落地不仅需要掌握基础API用法,更要兼顾可维护性、兼容性与用户体验。直接零散调用vibrator API会导致代码冗余、异常处理混乱、设备适配不足等问题,因此封装一套标准化、可复用的震动工具类,是鸿蒙震动技术落地的最优路径。

结合喵屿项目的实战经验,我们围绕"易用性、健壮性、可扩展性"三大核心,设计并实现了一套增强版震动工具类,覆盖开发中常见的震动场景,同时规避各类潜在问题,可直接集成到任意鸿蒙应用中使用。

工具类设计思路

针对项目开发中震动反馈的痛点,工具类设计遵循以下核心思路,确保落地高效、体验优质:

  1. 标准化封装:将震动相关的所有操作(启动、停止、兼容性检测)统一封装,避免代码重复编写,降低维护成本,同时统一交互体验。

  2. 类型安全约束:通过枚举定义震动效果、震动用途,避免开发者因传入错误参数导致功能异常,同时提供清晰的类型提示,提升开发效率。

  3. 异步化适配:采用Promise封装所有异步操作,替代传统回调函数,解决回调嵌套问题,更符合现代前端开发习惯,便于结合业务逻辑串联执行。

  4. 全链路异常防护:对所有API调用添加异常捕获,避免因设备不支持、权限缺失等问题导致应用崩溃,同时输出清晰的错误日志,便于问题排查。

  5. 设备兼容性适配:内置设备震动支持性、预设效果支持性检测,自动跳过不支持的操作,确保在不同鸿蒙设备上均能稳定运行,无需额外开发适配逻辑。

  6. 场景化快捷适配:针对按钮点击、长按、操作成功/失败、通知提醒等高频场景,提供一键调用的快捷方法,简化开发流程,无需重复配置参数。

增强版震动工具类(可直接复制复用)

基于上述设计思路,我们实现了功能全面、健壮性强的震动工具类,涵盖所有主流震动场景,同时优化了代码冗余,提升了可扩展性,具体实现如下:

import { vibrator } from "@kit.SensorServiceKit";
import { BusinessError } from "@kit.BasicServicesKit";

/**
 * 预设震动效果枚举(统一管理系统预设效果,避免硬编码)
 * 涵盖基础震动、通知类、交互类三大场景,适配绝大多数应用需求
 */
export enum VibrateEffect {
  // 基础震动效果(适用于简单反馈)
  SOFT = 'haptic.effect.soft',      // 轻微震动(低强度,不突兀)
  HARD = 'haptic.effect.hard',      // 强烈震动(高强度,用于重要反馈)
  SHARP = 'haptic.effect.sharp',    // 尖锐震动(短促有力,用于精准交互)
  
  // 通知类震动效果(适用于操作结果反馈)
  NOTICE_SUCCESS = 'haptic.notice.success',  // 成功通知(柔和,传递正向反馈)
  NOTICE_WARNING = 'haptic.notice.warning',  // 警告通知(中等强度,提醒注意)
  NOTICE_FAIL = 'haptic.notice.fail',        // 失败通知(急促,传递异常反馈)
  
  // 交互类震动效果(适用于用户主动操作)
  INTERACTION_CLICK = 'haptic.interaction.click',  // 点击震动(短促,确认操作)
  INTERACTION_LONG_PRESS = 'haptic.interaction.longPress',  // 长按震动(稍长,区分点击与长按)
}

/**
 * 震动用途枚举(明确震动场景,适配系统权限与资源分配)
 */
export enum VibrateUsage {
  PHYSICAL_FEEDBACK = 'physicalFeedback',  // 物理反馈(默认,适用于交互操作)
  ALARM = 'alarm',                        // 闹钟震动(高强度,用于唤醒)
  RINGTONE = 'ringtone',                  // 铃声震动(与铃声同步,提升提醒效果)
  NOTIFICATION = 'notification',          // 通知震动(中等强度,不打扰用户)
}

/**
 * 增强型震动工具类(全局复用,统一管理震动逻辑)
 * 提供完整的震动控制、兼容性检测、快捷调用能力,适配鸿蒙全机型
 */
export class VibrateUtil {
  /**
   * 检查当前设备是否支持震动功能
   * @returns Promise<boolean> 设备是否支持震动(true:支持,false:不支持)
   */
  public static async isVibratorSupported(): Promise<boolean> {
    return new Promise((resolve) => {
      try {
        // 以常用的轻微震动效果为检测基准,判断设备是否具备震动能力
        vibrator.isSupportEffect(VibrateEffect.SOFT, (err: BusinessError<void>, state: boolean) => {
          if (err) {
            console.error(`设备震动支持性检测失败:${err.code} - ${err.message}`);
            resolve(false);
            return;
          }
          resolve(state);
        });
      } catch (error) {
        console.error(`设备震动检测异常:${(error as BusinessError).code} - ${(error as BusinessError).message}`);
        resolve(false);
      }
    });
  }

  /**
   * 检查指定预设震动效果是否被当前设备支持
   * @param effect 震动效果(从VibrateEffect枚举中取值)
   * @returns Promise<boolean> 该效果是否支持(true:支持,false:不支持)
   */
  public static async isEffectSupported(effect: VibrateEffect): Promise<boolean> {
    return new Promise((resolve) => {
      try {
        vibrator.isSupportEffect(effect, (err: BusinessError<void>, state: boolean) => {
          if (err) {
            console.error(`震动效果${effect}检测失败:${err.code} - ${err.message}`);
            resolve(false);
            return;
          }
          resolve(state);
        });
      } catch (error) {
        console.error(`震动效果${effect}检测异常:${(error as BusinessError).code} - ${(error as BusinessError).message}`);
        resolve(false);
      }
    });
  }

  /**
   * 时长震动(自定义震动持续时间,适用于基础反馈场景)
   * @param duration 震动持续时间(单位:毫秒,建议10-500ms)
   * @param usage 震动用途(默认:物理反馈,根据场景灵活选择)
   * @returns Promise<boolean> 震动操作是否执行成功(true:成功,false:失败)
   */
  public static async vibrateByDuration(
    duration: number,
    usage: VibrateUsage = VibrateUsage.PHYSICAL_FEEDBACK
  ): Promise<boolean> {
    // 校验时长合理性,避免无效震动(过短无感知,过长影响体验)
    if (duration < 10 || duration > 500) {
      console.warn(`震动时长${duration}ms不合理,建议设置10-500ms`);
      return false;
    }

    return new Promise((resolve) => {
      try {
        vibrator.startVibration(
          { type: 'time', duration: duration },
          { usage: usage },
          (error: BusinessError<void>) => {
            if (error) {
              console.error(`时长震动启动失败:${error.code} - ${error.message}`);
              resolve(false);
              return;
            }
            console.info(`时长${duration}ms震动启动成功`);
            resolve(true);
          }
        );
      } catch (error) {
        console.error(`时长震动执行异常:${(error as BusinessError).code} - ${(error as BusinessError).message}`);
        resolve(false);
      }
    });
  }

  /**
   * 预设效果震动(使用系统预设效果,统一交互体验,推荐优先使用)
   * @param effect 震动效果(从VibrateEffect枚举中取值)
   * @param count 震动次数(默认1次,多次震动适用于强提醒场景)
   * @param usage 震动用途(默认:物理反馈)
   * @returns Promise<boolean> 震动操作是否执行成功(true:成功,false:失败)
   */
  public static async vibrateByEffect(
    effect: VibrateEffect,
    count: number = 1,
    usage: VibrateUsage = VibrateUsage.PHYSICAL_FEEDBACK
  ): Promise<boolean> {
    // 先检测当前设备是否支持该预设效果,避免无效调用
    const isSupported = await this.isEffectSupported(effect);
    if (!isSupported) {
      console.warn(`当前设备不支持${effect}震动效果,已跳过执行`);
      return false;
    }

    // 校验震动次数合理性
    if (count < 1 || count > 5) {
      console.warn(`震动次数${count}不合理,建议设置1-5次`);
      return false;
    }

    return new Promise((resolve) => {
      try {
        vibrator.startVibration(
          { type: 'preset', effectId: effect, count: count },
          { usage: usage },
          (error: BusinessError<void>) => {
            if (error) {
              console.error(`预设效果${effect}震动启动失败:${error.code} - ${error.message}`);
              resolve(false);
              return;
            }
            console.info(`预设效果${effect}震动启动成功,共${count}`);
            resolve(true);
          }
        );
      } catch (error) {
        console.error(`预设效果${effect}震动执行异常:${(error as BusinessError).code} - ${(error as BusinessError).message}`);
        resolve(false);
      }
    });
  }

  /**
   * 自定义模式震动(灵活配置震动/暂停节奏,适用于复杂反馈场景)
   * @param pattern 震动模式数组(交替表示震动、暂停的时长,单位:毫秒)
   * @param repeat 重复次数(-1表示无限循环,0表示不重复,建议不超过5次)
   * @param usage 震动用途(默认:物理反馈)
   * @returns Promise<boolean> 震动操作是否执行成功(true:成功,false:失败)
   */
  public static async vibrateByPattern(
    pattern: number[],
    repeat: number = -1,
    usage: VibrateUsage = VibrateUsage.PHYSICAL_FEEDBACK
  ): Promise<boolean> {
    // 校验模式数组合理性(长度至少2,确保有震动有暂停)
    if (!pattern || pattern.length < 2) {
      console.warn(`自定义震动模式数组无效,需至少包含2个元素(震动+暂停)`);
      return false;
    }

    // 校验重复次数合理性
    if (repeat < -1 || repeat > 5) {
      console.warn(`重复次数${repeat}不合理,建议设置-1(无限循环)或0-5次`);
      return false;
    }

    return new Promise((resolve) => {
      try {
        vibrator.startVibration(
          { type: 'pattern', pattern: pattern, repeat: repeat },
          { usage: usage },
          (error: BusinessError<void>) => {
            if (error) {
              console.error(`自定义模式震动启动失败:${error.code} - ${error.message}`);
              resolve(false);
              return;
            }
            console.info(`自定义模式震动启动成功,重复次数:${repeat === -1 ? '无限' : repeat}`);
            resolve(true);
          }
        );
      } catch (error) {
        console.error(`自定义模式震动执行异常:${(error as BusinessError).code} - ${(error as BusinessError).message}`);
        resolve(false);
      }
    });
  }

  /**
   * 停止震动(按需停止指定类型或所有震动,避免无效震动消耗电量)
   * @param stopMode 停止模式(all:停止所有;time:停止时长震动;preset:停止预设效果震动;pattern:停止自定义模式震动)
   * @returns Promise<boolean> 停止操作是否执行成功(true:成功,false:失败)
   */
  public static async stopVibration(
    stopMode: 'all' | 'time' | 'preset' | 'pattern' = 'all'
  ): Promise<boolean> {
    return new Promise((resolve) => {
      try {
        vibrator.stopVibration(stopMode, (error: BusinessError<void>) => {
          if (error) {
            console.error(`停止${stopMode}类型震动失败:${error.code} - ${error.message}`);
            resolve(false);
            return;
          }
          console.info(`${stopMode}类型震动已成功停止`);
          resolve(true);
        });
      } catch (error) {
        console.error(`停止${stopMode}类型震动异常:${(error as BusinessError).code} - ${(error as BusinessError).message}`);
        resolve(false);
      }
    });
  }

  // ———— 高频场景快捷方法(简化开发,一键调用,无需重复配置) ————
  /**
   * 按钮点击震动(快捷调用,适用于所有按钮点击交互)
   * @returns Promise<boolean> 震动操作是否执行成功
   */
  public static async buttonClick(): Promise<boolean> {
    return this.vibrateByEffect(VibrateEffect.INTERACTION_CLICK);
  }

  /**
   * 按钮长按震动(快捷调用,区分点击与长按操作,提升交互辨识度)
   * @returns Promise<boolean> 震动操作是否执行成功
   */
  public static async buttonLongPress(): Promise<boolean> {
    return this.vibrateByEffect(VibrateEffect.INTERACTION_LONG_PRESS);
  }

  /**
   * 操作成功反馈震动(快捷调用,适用于表单提交、任务完成等场景)
   * @returns Promise<boolean> 震动操作是否执行成功
   */
  public static async successFeedback(): Promise<boolean> {
    return this.vibrateByEffect(VibrateEffect.NOTICE_SUCCESS, 1, VibrateUsage.NOTIFICATION);
  }

  /**
   * 操作警告反馈震动(快捷调用,适用于参数错误、操作提醒等场景)
   * @returns Promise<boolean> 震动操作是否执行成功
   */
  public static async warningFeedback(): Promise<boolean> {
    return this.vibrateByEffect(VibrateEffect.NOTICE_WARNING, 1, VibrateUsage.NOTIFICATION);
  }

  /**
   * 操作失败反馈震动(快捷调用,适用于提交失败、接口异常等场景)
   * @returns Promise<boolean> 震动操作是否执行成功
   */
  public static async failFeedback(): Promise<boolean> {
    return this.vibrateByEffect(VibrateEffect.NOTICE_FAIL, 1, VibrateUsage.NOTIFICATION);
  }

  /**
   * 轻微震动(快捷调用,适用于次要交互、弱反馈场景)
   * @returns Promise<boolean> 震动操作是否执行成功
   */
  public static async softVibrate(): Promise<boolean> {
    return this.vibrateByEffect(VibrateEffect.SOFT);
  }

  /**
   * 强烈震动(快捷调用,适用于重要提醒、强反馈场景)
   * @returns Promise<boolean> 震动操作是否执行成功
   */
  public static async hardVibrate(): Promise<boolean> {
    return this.vibrateByEffect(VibrateEffect.HARD);
  }
}

工具类实战使用示例

结合喵屿项目的实际交互场景,以下示例展示了工具类在不同业务场景中的使用方式,简洁高效,可直接复制到项目中适配自身业务:

示例1:按钮点击交互(最常用场景)

适用于应用内所有按钮点击反馈,通过短促震动确认用户操作,提升交互质感:

// 导入工具类(根据项目路径调整导入路径)
import { VibrateUtil } from '../utils/VibrateUtil';

// 按钮组件使用
Button('保存')
  .width(120)
  .height(40)
  .onClick(async () => {
    // 先触发点击震动,再执行业务逻辑
    await VibrateUtil.buttonClick();
    // 执行相关业务操作
    console.log('保存数据');
  });

在这里插入图片描述

示例2:操作结果反馈(成功/失败场景)

适用于表单提交、数据保存、接口请求等场景,通过不同震动效果传递操作结果,减少视觉依赖:

// 模拟表单提交业务
async function submitPetInfo(petInfo: object) {
  try {
    // 模拟接口请求
    const response = await fetch('/api/pet/info', {
      method: 'POST',
      body: JSON.stringify(petInfo)
    });
    
    if (response.ok) {
      // 提交成功,触发成功震动反馈
      await VibrateUtil.successFeedback();
      console.log('宠物信息提交成功');
    } else {
      // 提交失败,触发失败震动反馈
      await VibrateUtil.failFeedback();
      console.log('宠物信息提交失败');
    }
  } catch (error) {
    // 异常情况,触发失败震动反馈
    await VibrateUtil.failFeedback();
    console.error('宠物信息提交异常:', error);
  }
}
示例3:自定义节奏震动(特殊场景)

适用于特殊提醒场景(如猫咪状态提醒、定时喂养提醒),通过自定义震动节奏实现差异化反馈:

// 模拟猫咪饥饿提醒震动(心跳式节奏)
async function petHungryRemind() {
  // 震动模式:震动100ms → 暂停100ms → 震动100ms → 暂停800ms,重复3次
  const pattern = [100, 100, 100, 800];
  await VibrateUtil.vibrateByPattern(pattern, 3);
  console.log('猫咪饥饿提醒震动触发');
}

// 调用提醒方法
petHungryRemind();
示例4:手动停止震动(特殊需求场景)

适用于无限循环震动场景(如紧急提醒),允许用户手动停止震动,提升用户体验:

// 启动无限循环震动(紧急提醒)
async function startEmergencyRemind() {
  const pattern = [200, 300]; // 震动200ms,暂停300ms,无限循环
  await VibrateUtil.vibrateByPattern(pattern, -1);
}

// 停止所有震动(用户手动触发)
Button('停止提醒')
  .onClick(async () => {
    await VibrateUtil.stopVibration('all');
    console.log('所有震动已停止');
  });

注意事项

  1. 权限配置:在 module.json5 文件中配置震动权限
"requestPermissions": [
  {
    "name": "ohos.permission.VIBRATE"
  }
]
  1. 设备兼容性:不同设备的震动效果可能有所差异,建议在多种设备上测试

  2. 用户设置:尊重用户的系统震动设置,避免在用户关闭震动时强制触发震动

  3. 适度使用:震动反馈应适度使用,过度使用可能会引起用户不适

  4. 错误处理:始终添加错误处理,确保震动操作不会影响应用的正常运行

总结

震动反馈是提升应用用户体验的重要手段,通过合理使用 HarmonyOS 提供的震动 API,可以为用户提供更加丰富、直观的交互体验。本文详细介绍了 HarmonyOS 中的震动 API,包括核心模块、震动类型和核心 API 的使用方法,并提供了一个功能全面、设计合理的震动工具类。

通过本文的介绍,相信开发者们已经对鸿蒙震动技术有了全面的了解,并能够在自己的项目中实现各种震动效果,为用户提供更加优质的触觉交互体验。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐