最近接到一个需求是改造项目已有的一个功能,是一个悬浮小球,可以满屏拖拽,快到屏幕边缘还要能吸附。听起来还挺简单的,本来设置可以拖拽的app-region就可以,但是结合鼠标移入要展开一个供用户交互的操作面板,鼠标移出还要隐藏操作面板,就得自己处理各种鼠标事件了:
document.addEventListener('mousedown', () => {
document.addEventListener('mousemove', handleMouseMove)
document.addEventListener('mouseup', handleMouseUp)
})
然后需求还要提前预判操作面板打开是否会超出屏幕,否则将展示不全,咱就是说也是够贴心的。于是在抬起鼠标也就是最终确定移动位置的时候,我尽可能详尽的记录了 鼠标位置 / 窗口位置 /鼠标相对于元素位置 等信息, 以方便做各种计算。
const eventInfo: MouseInfo = {
windowX: window.screenX,
windowY: window.screenY,
windowWidth: window.outerWidth,
windowHeight: window.outerHeight,
mouseInMiniX: e.offsetX,
mouseInMiniY: e.offsetY,
mouseScreenX: e.screenX,
mouseScreenY: e.screenY,
miniRectWidth: miniRec.width,
miniRectHeight: miniRec.height
}
然后既然是悬浮窗,意味着这个小球元素得用一种脱离文档流的绝对布局,其他附属元素要基于小球或者容器定位, 我这边就用一个动态样式去改变小球的位置,取决于小球处于屏幕的位置,但是现在有个问题是,小球设置为目标位置后,有时候会在原始位置快速闪一下,这个还没有找到解决方案,但是大概能理解产生的原因,需要屏蔽原始位置的渲染帧。
<FloatBar
:style="{ transform: `translate(${transform.mini.x}, ${transform.mini.y})` }"
>
</FloatBar>
最后是由于悬浮窗的初始位置在桌面靠近边缘的位置,这时候用户有可能改变屏幕缩放,比如改成200%, 这时候悬浮窗就有可能显示在工作区以外了,所以需要适配缩放率,重置一下窗口的位置
screen.on('display-metrics-changed', (event, display, changedMetrics) => {
this.window.setBounds({
x,
y,
width,
height
})
})
另外还有鼠标穿透事件需要处理,因为窗口是方形的,悬浮球通常是圆形的,如果不处理鼠标穿透,周围会有一圈看似透明的区域,却不能触发鼠标点击到下方窗口,所以基本要在窗口 focus的时候设置window.api?.ignoreMouse(true), 窗口失焦的时候设置 window.api?.ignoreMouse(false).
总结,一个 browserWindow API 确实是可以灵活的实现任何形式和效果的窗口,不过需要很细心的处理各个窗口之间的关系,以及窗口与屏幕的关系。更没提到,还要大量处理悬浮窗搭配主窗功能的进程通讯/同步。