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

View File

@@ -263,6 +263,37 @@ describe('glass optical surface discovery', () => {
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', () => {
const surface = document.createElement('section')
surface.dataset.glassOpticalSurface = ''

View File

@@ -193,15 +193,17 @@ export function useGlassOpticalInteractionSource(): GlassOpticalInteractionSourc
return touchOwners.get(changedTouch.identifier) ?? null
}
const isTouchInteractionEvent = (event: PointerEvent | TouchEvent): event is TouchEvent =>
event.type.startsWith('touch')
const dispatch = (event: PointerEvent | TouchEvent) => {
const owner =
event instanceof TouchEvent
? resolveTouchOwner(event)
: resolvePointOwner((event as PointerEvent).clientX, (event as PointerEvent).clientY)
const owner = isTouchInteractionEvent(event)
? resolveTouchOwner(event)
: resolvePointOwner(event.clientX, event.clientY)
if (!owner) return
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)
}
}
@@ -1901,8 +1903,7 @@ export function useGlassOpticalRenderer(options: UseGlassOpticalRendererOptions)
? candidates.findIndex(candidate => candidate.key === activeInteractionClip)
: -1
if (activeIndex > 0) candidates.unshift(...candidates.splice(activeIndex, 1))
const maxCount =
window.innerWidth <= 600 ? GLASS_OPTICAL_MAX_SURFACES_MOBILE : GLASS_OPTICAL_MAX_SURFACES_DESKTOP
const maxCount = window.innerWidth <= 600 ? GLASS_OPTICAL_MAX_SURFACES_MOBILE : GLASS_OPTICAL_MAX_SURFACES_DESKTOP
interactionClips = candidates.slice(0, maxCount)
}