electron perfect drag and drop : μια πιο κομψή υλοποίηση από το electron-drag
Ο υπεύθυνος προϊόντος ζήτησε η γραμμή εργαλείων κάτω από το παράθυρο electron να μπλοκάρει το δεξί κουμπί του ποντικιού.
Η εγγενής μεταφορά και απόθεση σε ηλεκτρόνιο κάτω από το παράθυρο ( app-region:drag
) τρώει το συμβάν κλικ και δεν υπάρχει τρόπος να γίνει μπλοκάρισμα του δεξιού κλικ. Θα πρέπει να εφαρμόσετε το drag and drop χειροκίνητα.
Αν χρησιμοποιήσετε το συμβάν mousemove για την υλοποίηση της απαίτησης, όταν το ποντίκι μετακινείται γρήγορα, βγαίνει από την περιοχή του παραθύρου και το παράθυρο δεν ακολουθεί πλέον το ποντίκι.
Έχω υλοποιήσει αυτή την απαίτηση με βάση το συμβάν του δείκτη, χρησιμοποιώντας το setPointerCapture για τη σύλληψη της θέσης του ποντικιού εκτός του BrowserWindow, και λειτουργεί τέλεια.
Αυτή η λύση είναι καλύτερη από τη λύση electron-drag: έχει εξαρτώμενες από την πλατφόρμα δυαδικές εξαρτήσεις και μπορεί να είναι μπελάς η αναβάθμιση και η συσκευασία.
Διαπίστωσα ότι κανείς δεν έχει αναφερθεί ακόμη σε αυτή τη λύση στο διαδίκτυο, γι' αυτό την αναφέρω στο blog και την μοιράζομαι.
Οι διαφορές στον κώδικα μεταξύ των υλοποιήσεων παρατίθενται παρακάτω μόνο για λόγους αναφοράς. Ο βασικός κώδικας είναι 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