Skip to content

Commit d885848

Browse files
Merge pull request #4998 from traPtitech/fix/relax-drag-end-detection
fix: ドラッグの終了判定を緩和する
2 parents 57d6ef2 + 05dc12c commit d885848

3 files changed

Lines changed: 111 additions & 53 deletions

File tree

src/components/Main/NavigationBar/DesktopNavigationBar.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
@pointerdown="onDragStart"
4343
@pointermove="onDragging"
4444
@pointerup="onDragEnd"
45+
@pointercancel="onDragEnd"
4546
@dblclick="initializeNavigationWidth"
4647
/>
4748
</div>

src/components/Main/NavigationBar/composables/useNavigationResizer.ts

Lines changed: 29 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
import { computed, onUnmounted, ref } from 'vue'
1+
import { computed, onUnmounted } from 'vue'
22

33
import { useWindowSize } from '@vueuse/core'
44

5+
import useDragging from '/@/composables/dom/useDragging'
56
import { createAnimationFrameController } from '/@/lib/dom/animationFrame'
67
import {
78
MAX_NAVIGATION_WIDTH_RATIO,
@@ -40,62 +41,37 @@ const useNavigationResizer = () => {
4041
return clampWidth(navigationWidth.value)
4142
})
4243

43-
const isResizing = ref(false)
44-
let pointerId: null | number = null
44+
const { isDragging, onDragStart, onDragging, onDragEnd } = useDragging({
45+
targetRef: resizerRef,
46+
onDragStart: () => {
47+
document.body.style.cursor = 'e-resize'
48+
document.body.style.userSelect = 'none'
49+
if (!isNavigationClosed.value) updateNavigationLeft()
50+
},
51+
onDragging: (e: PointerEvent) => {
52+
animationFrame.request(() => {
53+
const width = e.clientX - navigationLeft.value
54+
55+
if (width <= NAVIGATION_CLOSING_THRESHOLD) {
56+
closeNavigation()
57+
} else {
58+
setNavigationWidth(width)
59+
}
60+
})
61+
},
62+
onDragEnd: () => {
63+
document.body.style.cursor = ''
64+
document.body.style.userSelect = ''
65+
if (navigationWidth.value > 0) saveNavigationWidth()
66+
}
67+
})
4568

46-
const cleanup = () => {
69+
onUnmounted(() => {
4770
animationFrame.cancel()
48-
49-
document.body.style.cursor = ''
50-
document.body.style.userSelect = ''
51-
52-
if (!pointerId || !resizerRef.value?.hasPointerCapture(pointerId)) return
53-
resizerRef.value?.releasePointerCapture(pointerId)
54-
}
55-
56-
const onDragStart = (e: PointerEvent) => {
57-
isResizing.value = true
58-
59-
if (!isNavigationClosed.value) updateNavigationLeft()
60-
61-
document.body.style.cursor = 'e-resize'
62-
document.body.style.userSelect = 'none'
63-
64-
pointerId = e.pointerId
65-
resizerRef.value?.setPointerCapture(pointerId)
66-
67-
e.preventDefault()
68-
}
69-
70-
const onDragging = (e: PointerEvent) => {
71-
if (!isResizing.value) return
72-
73-
animationFrame.request(() => {
74-
const width = e.clientX - navigationLeft.value
75-
76-
if (width <= NAVIGATION_CLOSING_THRESHOLD) {
77-
closeNavigation()
78-
} else {
79-
setNavigationWidth(width)
80-
}
81-
})
82-
83-
e.preventDefault()
84-
}
85-
86-
const onDragEnd = () => {
87-
if (!isResizing.value) return
88-
89-
if (navigationWidth.value > 0) saveNavigationWidth()
90-
91-
isResizing.value = false
92-
cleanup()
93-
}
94-
95-
onUnmounted(cleanup)
71+
})
9672

9773
return {
98-
isNavigationResizing: isResizing,
74+
isNavigationResizing: isDragging,
9975
navigationWidth: clampedNavigationWidth,
10076
onDragStart,
10177
onDragging,

src/composables/dom/useDragging.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { type MaybeRefOrGetter, onBeforeUnmount, ref, toValue } from 'vue'
2+
3+
import { useEventListener } from '@vueuse/core'
4+
5+
interface UseDraggingOptions {
6+
targetRef: MaybeRefOrGetter<HTMLElement | null | undefined>
7+
onDragStart?: (e: PointerEvent) => void
8+
onDragging?: (e: PointerEvent) => void
9+
onDragEnd?: () => void
10+
}
11+
12+
const useDragging = ({
13+
targetRef,
14+
onDragStart,
15+
onDragging,
16+
onDragEnd
17+
}: UseDraggingOptions) => {
18+
const isDragging = ref(false)
19+
let pointerId: null | number = null
20+
21+
const handleDragStart = (e: PointerEvent) => {
22+
const target = toValue(targetRef)
23+
24+
isDragging.value = true
25+
26+
pointerId = e.pointerId
27+
target?.setPointerCapture(pointerId)
28+
29+
onDragStart?.(e)
30+
e.preventDefault()
31+
}
32+
33+
const handleDragging = (e: PointerEvent) => {
34+
if (!isDragging.value) return
35+
36+
onDragging?.(e)
37+
e.preventDefault()
38+
}
39+
40+
const handleDragEnd = () => {
41+
if (!isDragging.value) return
42+
isDragging.value = false
43+
44+
const target = toValue(targetRef)
45+
46+
if (pointerId && target?.hasPointerCapture(pointerId)) {
47+
target?.releasePointerCapture(pointerId)
48+
}
49+
50+
pointerId = null
51+
52+
onDragEnd?.()
53+
}
54+
55+
const handleVisibilityChange = () => {
56+
if (document.hidden) {
57+
handleDragEnd()
58+
}
59+
}
60+
61+
const handleKeyDown = (e: KeyboardEvent) => {
62+
if (e.key === 'Escape') {
63+
handleDragEnd()
64+
}
65+
}
66+
67+
useEventListener(['blur', 'pagehide'], handleDragEnd)
68+
useEventListener(document, 'contextmenu', handleDragEnd)
69+
useEventListener(document, 'visibilitychange', handleVisibilityChange)
70+
useEventListener(document, 'keydown', handleKeyDown)
71+
onBeforeUnmount(handleDragEnd)
72+
73+
return {
74+
isDragging,
75+
onDragStart: handleDragStart,
76+
onDragging: handleDragging,
77+
onDragEnd: handleDragEnd
78+
}
79+
}
80+
81+
export default useDragging

0 commit comments

Comments
 (0)