electron perfect drag and drop : une mise en œuvre plus élégante que electron-drag
Le chef de produit a demandé que la barre d'outils sous la fenêtre électronique bloque le bouton droit de la souris.
Le glisser-déposer natif dans l'électron sous la fenêtre ( app-region:drag
) consomme l'événement de clic et il n'y a aucun moyen de bloquer le clic droit. Vous devez mettre en œuvre le glisser-déposer manuellement.
Si vous utilisez l'événement mousemove pour mettre en œuvre cette exigence, lorsque la souris se déplace rapidement, elle sort du champ de la fenêtre et la fenêtre ne suit plus la souris.
J'ai mis en œuvre cette exigence en me basant sur l'événement du pointeur, en utilisant setPointerCapture pour capturer la position de la souris en dehors de la BrowserWindow, et cela fonctionne parfaitement.
Cette solution est meilleure que la solution electron-drag : elle a des dépendances binaires dépendantes de la plateforme et peut être une douleur à mettre à jour et à empaqueter.
J'ai constaté que personne n'avait encore mentionné cette solution sur le web, alors j'en parle sur mon blog et je la partage.
Les différences de code entre les implémentations sont énumérées ci-dessous à titre de référence uniquement. Le code de base est web/src/lib/drag.coffee
et 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
principal/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