在鸿蒙(HarmonyOS)应用向 PC 端和平板端扩展时,鼠标悬停(Hover)是提升桌面级交互体验的关键。开发者可以通过 onHover 事件、hoverEffect 属性以及 onMouse 高级事件,实现丰富的悬停反馈。
以下是实现 PC 端特有交互反馈的核心策略与代码示例:
一、 基础悬停状态反馈(onHover 事件)
通过 onHover 回调,可以精准感知鼠标进入或离开组件的瞬间。常用于自定义背景色变化、文字变色或触发预加载逻辑。
核心代码示例:
javascript
Button('提交')
.onHover((isHover: boolean) => {
if (isHover) {
this.bgColor = '#e1e1e1'; // 鼠标进入时变色
} else {
this.bgColor = '#ffffff'; // 鼠标离开时恢复
}
})
二、 系统级标准悬浮态(hoverEffect 属性)
如果希望应用遵循鸿蒙 PC 端原生的视觉规范,可以直接使用 hoverEffect 属性。系统会自动处理按下与松开时的状态切换(按下时效果消失,松开时恢复)。
HoverEffect.Scale:悬停时组件轻微放大。HoverEffect.Highlight:悬停时组件高亮。
核心代码示例:
javascript
Column() {
Text('系统标准缩放效果')
}
.hoverEffect(HoverEffect.Scale) // 自动应用系统级缩放反馈
三、 区分输入源:鼠标 vs 手写笔
在鸿蒙中,onHover 支持传入 HoverEvent 对象。通过 event.sourceTool 属性,可以区分当前是鼠标还是手写笔触发的悬停,从而提供差异化的 UI 反馈。
核心代码示例:
javascript
Button('悬停测试')
.onHover((isHover: boolean, event: HoverEvent) => {
if (isHover) {
if (event.sourceTool == SourceTool.Pen) {
this.hoverText = '手写笔悬停';
this.color = Color.Pink;
} else if (event.sourceTool == SourceTool.MOUSE) {
this.hoverText = '鼠标悬停';
this.color = Color.Red;
}
} else {
this.hoverText = '无悬停';
this.color = Color.Blue;
}
})
四、 进阶:防抖处理与右键菜单(PC 专属)
在 PC 端,鼠标快速滑动时可能会频繁触发 onHover。为了防止性能抖动,建议结合定时器进行防抖处理;同时可结合 onContextMenu 实现 PC 端专属的右键菜单。
核心代码示例:
javascript
Column() {
Text(this.doc.title)
.fontColor(this.isHovered ? '#007DFF' : '#333')
.backgroundColor(this.isHovered ? '#E6F7FF' : '#FFFFFF')
// 鼠标悬停事件(带防抖)
.onHover((isHover: boolean) => {
if (!DeviceDetector.isDesktop()) return; // 仅PC环境启用
if (this.hoverTimer !== -1) clearTimeout(this.hoverTimer);
this.hoverTimer = setTimeout(() => {
this.isHovered = isHover;
if (isHover) {
Document.preloadDetail(this.doc.id); // 悬停时预加载详情
}
}, 50); // 50ms 防抖
})
// 右键点击触发菜单
.onContextMenu(() => {
if (DeviceDetector.isDesktop()) {
this.showContextMenu = true;
setTimeout(() => { this.showContextMenu = false; }, 3000);
}
})
}
💡 PC 端悬停交互开发建议
- 防抖机制 :在列表或瀑布流中,务必对
onHover内的复杂逻辑(如网络请求、预加载)加上 50ms 左右的防抖,避免快速滑动时引发 UI 卡顿。 - 设备隔离 :使用
DeviceDetector.isDesktop()等判断,确保悬停特效仅在 PC 或平板端生效,避免影响手机端的触摸体验。 - 自动隐藏:对于悬停触发的右键菜单或 Tooltip,建议设置 3 秒左右的自动隐藏逻辑,防止焦点丢失后 UI 元素残留。
五、 精细坐标追踪:onHoverMove 事件
在 PC 端,有时不仅需要知道"是否悬停",还需要知道鼠标在组件内的"精确位置"。从 API version 15 开始,鸿蒙提供了 onHoverMove 事件,常用于实现光效跟随、自定义 Tooltip 定位等高级视觉反馈。
核心代码示例:
javascript
@Entry
@Component
struct OnHoverMoveEventExample {
@State hoverMoveText: string = '';
build() {
Column({ space: 20 }) {
Button('onHoverMove 追踪')
.width(200).height(80)
.onHoverMove((event: HoverEvent) => {
// 实时获取鼠标在组件内、窗口内、屏幕内的绝对坐标
this.hoverMoveText = `组件内XY: (${event.x}, ${event.y})\n` +
`窗口XY: (${event.windowX}, ${event.windowY})`;
})
Text(this.hoverMoveText).fontSize(14).fontColor(Color.Gray)
}
.padding(30)
}
}
六、 复杂交互联动:onMouse 综合事件
当需要在同一个组件上同时处理"悬停、按下、移动、抬起"等多种鼠标动作时,使用 onHover 会显得力不从心。此时应使用 onMouse 事件,它通过 MouseEvent 对象提供了全方位的鼠标状态反馈。
核心代码示例:
javascript
Button('综合鼠标事件')
.width(200).height(100)
.onMouse((event?: MouseEvent) => {
if (event) {
// 1. 判断鼠标按键(左键、右键、中键等)
if (event.button === MouseButton.Left && event.action === MouseAction.Press) {
console.log('鼠标左键按下');
}
// 2. 判断鼠标动作(Hover, Move, Press, Release)
if (event.action === MouseAction.Hover) {
console.log('鼠标悬停');
}
// 3. 获取屏幕绝对坐标
console.log(`屏幕坐标: (${event.screenX}, ${event.screenY})`);
}
})
七、 嵌套组件的事件冒泡控制
在 PC 端复杂的嵌套布局中(例如:列表项内部包含一个"更多操作"按钮),鼠标悬停在内部按钮时,外部的列表项也会触发 onHover。为了隔离交互,需要调用 stopPropagation() 阻止事件冒泡。
核心代码示例:
javascript
Column() {
// 内部操作按钮
Button('更多')
.onMouse((event?: MouseEvent) => {
if (event) {
// 【关键】阻止事件向上冒泡,防止外层列表项同时触发高亮
event.stopPropagation();
}
})
.onClick(() => { /* 处理点击 */ })
}
.onHover((isHover: boolean) => {
// 只有当鼠标悬停在列表项空白处时才会触发,悬停在"更多"按钮上时不会触发
this.isItemHovered = isHover;
})
八、 滚轮缩放与键盘快捷键联动
PC 端的交互不仅限于鼠标悬停,滚轮(Mouse Wheel)和键盘快捷键(Key Events)同样是桌面级体验的核心。
核心代码示例:
javascript
Column() {
// 1. 鼠标滚轮缩放
Image($r('app.media.pcImage'))
.width(400 * this.scale).height(300 * this.scale)
.onMouseWheel((event) => {
if (event.deltaY < 0) {
this.scale = Math.min(this.scale + 0.1, 2); // 向上滚放大
} else {
this.scale = Math.max(this.scale - 0.1, 0.5); // 向下滚缩小
}
return true; // 阻止事件继续冒泡
})
// 2. 键盘快捷键监听
TextInput({ placeholder: '输入内容' })
.onKeyEvent((event) => {
// 监听 Ctrl+S 触发保存
if (event.keyCode === KeyCode.KEY_S && event.ctrlKey) {
promptAction.showToast({ message: '触发 Ctrl+S 保存' });
return true; // 阻止系统默认行为
}
return false;
})
}
💡 高阶交互架构建议
- 事件归一与职责分离 :简单的背景色切换使用
onHover;需要坐标追踪使用onHoverMove;涉及复杂按键和动作组合使用onMouse。避免在一个组件上滥用多个同类事件。 - 性能保护 :
onHoverMove和onMouse的触发频率极高。在这些回调中绝对不要进行耗时操作(如 DOM 重排、复杂计算或网络请求),仅用于更新轻量级的 UI 状态(如坐标文本、光效位置)。 - 多模态兼容 :在编写 PC 端交互时,始终通过
event.sourceTool或DeviceDetector做好环境隔离。确保鼠标专属的 Hover 效果不会在移动端或触屏设备上产生误触或视觉残留。