fix(glass): support pointer input in Safari (#599)

This commit is contained in:
InfinityPacer
2026-07-30 17:16:27 +08:00
committed by GitHub
parent 7e3e95fbcd
commit 32e4a9b817
2 changed files with 39 additions and 7 deletions
@@ -263,6 +263,37 @@ describe('glass optical surface discovery', () => {
scope.stop() scope.stop()
}) })
it('routes desktop pointer input when the browser does not expose TouchEvent', () => {
vi.stubGlobal('TouchEvent', undefined)
const scrollListener = vi.fn()
const scope = effectScope()
scope.run(() => {
const source = useGlassOpticalInteractionSource()
source.subscribe('scroll', scrollListener)
})
window.dispatchEvent(new MouseEvent('pointermove', { clientX: 400, clientY: 400 }))
expect(scrollListener).toHaveBeenCalledOnce()
scope.stop()
})
it('keeps touch ownership when the browser does not expose TouchEvent', () => {
vi.stubGlobal('TouchEvent', undefined)
const scrollListener = vi.fn()
const scope = effectScope()
scope.run(() => {
const source = useGlassOpticalInteractionSource()
source.subscribe('scroll', scrollListener)
})
dispatchTouchEvent('touchstart', [{ clientX: 400, clientY: 400, identifier: 7 }])
dispatchTouchEvent('touchmove', [{ clientX: 420, clientY: 420, identifier: 7 }])
expect(scrollListener).toHaveBeenCalledTimes(2)
scope.stop()
})
it('detects a target surface added directly', () => { it('detects a target surface added directly', () => {
const surface = document.createElement('section') const surface = document.createElement('section')
surface.dataset.glassOpticalSurface = '' surface.dataset.glassOpticalSurface = ''
+8 -7
View File
@@ -193,15 +193,17 @@ export function useGlassOpticalInteractionSource(): GlassOpticalInteractionSourc
return touchOwners.get(changedTouch.identifier) ?? null return touchOwners.get(changedTouch.identifier) ?? null
} }
const isTouchInteractionEvent = (event: PointerEvent | TouchEvent): event is TouchEvent =>
event.type.startsWith('touch')
const dispatch = (event: PointerEvent | TouchEvent) => { const dispatch = (event: PointerEvent | TouchEvent) => {
const owner = const owner = isTouchInteractionEvent(event)
event instanceof TouchEvent ? resolveTouchOwner(event)
? resolveTouchOwner(event) : resolvePointOwner(event.clientX, event.clientY)
: resolvePointOwner((event as PointerEvent).clientX, (event as PointerEvent).clientY)
if (!owner) return if (!owner) return
for (const listener of listeners[owner]) listener(event) for (const listener of listeners[owner]) listener(event)
if (event instanceof TouchEvent && (event.type === 'touchend' || event.type === 'touchcancel')) { if (isTouchInteractionEvent(event) && (event.type === 'touchend' || event.type === 'touchcancel')) {
for (const touch of Array.from(event.changedTouches)) touchOwners.delete(touch.identifier) for (const touch of Array.from(event.changedTouches)) touchOwners.delete(touch.identifier)
} }
} }
@@ -1901,8 +1903,7 @@ export function useGlassOpticalRenderer(options: UseGlassOpticalRendererOptions)
? candidates.findIndex(candidate => candidate.key === activeInteractionClip) ? candidates.findIndex(candidate => candidate.key === activeInteractionClip)
: -1 : -1
if (activeIndex > 0) candidates.unshift(...candidates.splice(activeIndex, 1)) if (activeIndex > 0) candidates.unshift(...candidates.splice(activeIndex, 1))
const maxCount = const maxCount = window.innerWidth <= 600 ? GLASS_OPTICAL_MAX_SURFACES_MOBILE : GLASS_OPTICAL_MAX_SURFACES_DESKTOP
window.innerWidth <= 600 ? GLASS_OPTICAL_MAX_SURFACES_MOBILE : GLASS_OPTICAL_MAX_SURFACES_DESKTOP
interactionClips = candidates.slice(0, maxCount) interactionClips = candidates.slice(0, maxCount)
} }