electron perfect drag and drop : по-елегантно изпълнение от electron-drag
Продуктовият мениджър поиска лентата с инструменти под прозореца на електрона да блокира десния бутон на мишката.
Нативното плъзгане и пускане в електронния прозорец под прозореца ( app-region:drag
) изяжда събитието за щракване и няма начин да се блокира щракването с десния бутон на мишката. Трябва да приложите плъзгането и пускането ръчно.
Ако използвате събитието mousemove за изпълнение на изискването, когато мишката се движи бързо, тя излиза от обхвата на прозореца и прозорецът вече не следва мишката.
Реализирах това изискване въз основа на събитието на показалеца, като използвах setPointerCapture за улавяне на позицията на мишката извън прозореца BrowserWindow, и то работи перфектно.
Това решение е по-добро от решението electron-drag: то има платформено зависими двоични зависимости и може да бъде неприятно за обновяване и пакетиране.
Открих, че никой все още не е споменал това решение в мрежата, затова пиша за него в блога си и го споделям.
Разликите в кода на реализациите са изброени по-долу само за справка. Основният код е web/src/lib/drag.coffee
и main/ipc/drag.coffee
.
web/src/lib/drag.coffee
import $on from '~/lib/on.coffee'
import ipc from '~/lib/ipc.coffee'
import platform from '@/config/platform.mjs'
{drag:{setBounds,getBounds}} = ipc
pointermove = 'pointermove'
IGNORE = new Set('SELECT BUTTON A INPUT TEXTAREA'.split ' ')
{round} = Math
export default main = (elem)=>
if platform!='win32'
return
elem.style.appRegion = 'no-drag'
moving = false
init_w = init_h = init_x = init_y = init_top = init_left = undefined
_move = (e)=>
{screenX,screenY} = e
setBounds(
round screenX-init_x+init_left
round screenY-init_y+init_top
init_w
init_h
)
return
down = (e)=>
if moving
return
if e.button!=0 # 鼠标左键
return
{target} = e
p = e.target
loop
{nodeName} = p
if IGNORE.has nodeName
return
if nodeName == 'BODY'
break
p = p.parentNode
moving = true
{screenX:init_x,screenY:init_y} = e
elem.setPointerCapture e.pointerId
elem.addEventListener pointermove,_move
{
x:init_left
y:init_top
height:init_h
width:init_w
} = await getBounds()
console.log await getBounds()
return
up = (e)=>
if moving
await _move(e)
elem.releasePointerCapture e.pointerId
elem.removeEventListener pointermove,_move
moving = false
return
$on elem,{
lostpointercapture:up
pointercancel:up
pointerdown:down
pointerup:up
}
return
main/ipc/drag.coffee
export getBounds = ->
@sender.getOwnerBrowserWindow().getBounds()
export setBounds = (x,y,width,height)->
win = @sender.getOwnerBrowserWindow()
# 不用 setPosition 是因为 https://github.com/electron/electron/issues/9477 browserWindow.setPosition(x,y) changed window size (windows/linux) with non default scaleLevel (125% for example)
win.setBounds {
x:Math.round(x)
y:Math.round(y)
width
height
}
return
main/ipc.coffee
`export * as drag from './ipc/drag.coffee'`
web/src/page/recbar.vue
<template lang="pug">
-nav(:class="{ pause }")
nav(:class="{ pause }" ref="nav")
</template>
<script lang="coffee">
import drag from '~/lib/drag.coffee'
nav = shallowRef()
export default {
setup: =>
onMounted =>
drag(nav.value)
return
{
nav
}
}
</script>
main/boot.coffee
_handle = (k, v)=>
if v instanceof Function
ipcMain.handle k.join('.'), (e,args)=>
v.apply(e,args)
for [name,func] from Object.entries v
_handle [...k, name], func
return
for [k,v] from Object.entries ipc
_handle([k], v)
web/src/lib/_on.coffee
export default (elem, dict)=>
for event,func of dict
elem.addEventListener(event, func)
=>
for event,func of dict
elem.removeEventListener(event, func)
web/src/lib/on.coffee
import $on from './_on.coffee'
export default (elem, dict)=>
unbind = $on elem, dict
onUnmounted unbind
unbind