Make coarse pointer check dynamic (#3572)
When a device has both a touch screen and a mouse, pointer: coarse evaluates to false and then doesn't change even if the user begins to use the touch screen. In this PR pointer: coarse is replaced with any-pointer: coarse, which checks if a touchscreen input is available. Event listeners for touchstart and mousemove update isCoursePointer dynamically. So if the user begins to move the mouse it changes to pointer: fine, if they then touch the screen the pointer is returned to coarse. https://github.com/tldraw/tldraw/assets/98838967/fb86bb44-ec11-4161-bb2f-0e8c3ee83eb6 ### Change Type <!-- ❗ Please select a 'Scope' label ❗️ --> - [ ] `sdk` — Changes the tldraw SDK - [x] `dotcom` — Changes the tldraw.com web app - [ ] `docs` — Changes to the documentation, examples, or templates. - [ ] `vs code` — Changes to the vscode plugin - [ ] `internal` — Does not affect user-facing stuff <!-- ❗ Please select a 'Type' label ❗️ --> - [ ] `bugfix` — Bug fix - [ ] `feature` — New feature - [x] `improvement` — Improving existing features - [ ] `chore` — Updating dependencies, other boring stuff - [ ] `galaxy brain` — Architectural changes - [ ] `tests` — Changes to any test code - [ ] `tools` — Changes to infrastructure, CI, internal scripts, debugging tools, etc. - [ ] `dunno` — I don't know ### Test Plan 1. Load tldraw on a device with both coarse and fine pointer inputs avaiable (e.g. an ipad with a keyboard and trackpad) 2. Switch between using the mouse and touch screen. 3. Handles on shapes should update dynamically. ### Release Notes - Add a brief release note for your PR here. --------- Co-authored-by: Mime Čuvalo <mimecuvalo@gmail.com> Co-authored-by: Steve Ruiz <steveruizok@gmail.com>
This commit is contained in:
parent
608f0210a0
commit
7442456d85
1 changed files with 56 additions and 15 deletions
|
@ -4,27 +4,68 @@ import { useEditor } from './useEditor'
|
||||||
/** @internal */
|
/** @internal */
|
||||||
export function useCoarsePointer() {
|
export function useCoarsePointer() {
|
||||||
const editor = useEditor()
|
const editor = useEditor()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// We'll track our own state for the pointer type
|
||||||
|
let isCoarse = editor.getInstanceState().isCoarsePointer
|
||||||
|
|
||||||
|
// 1.
|
||||||
|
// We'll use touch events / mouse events to detect coarse pointer.
|
||||||
|
|
||||||
|
// When the user touches the screen, we assume they have a coarse pointer
|
||||||
|
const handleTouchStart = () => {
|
||||||
|
if (isCoarse) return
|
||||||
|
isCoarse = true
|
||||||
|
editor.updateInstanceState({ isCoarsePointer: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the user moves the mouse, we assume they have a fine pointer
|
||||||
|
const handleMouseMove = () => {
|
||||||
|
if (!isCoarse) return
|
||||||
|
isCoarse = false
|
||||||
|
editor.updateInstanceState({ isCoarsePointer: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the listeners for touch and mouse events
|
||||||
|
window.addEventListener('touchstart', handleTouchStart)
|
||||||
|
window.addEventListener('mousemove', handleMouseMove)
|
||||||
|
|
||||||
|
// 2.
|
||||||
|
// We can also use the media query to detect / set the initial pointer type
|
||||||
|
// and update the state if the pointer type changes.
|
||||||
|
|
||||||
|
// We want the touch / mouse events to run even if the browser does not
|
||||||
|
// support matchMedia. We'll have to handle the media query changes
|
||||||
|
// conditionally in the code below.
|
||||||
|
const mql = window.matchMedia && window.matchMedia('(any-pointer: coarse)')
|
||||||
|
|
||||||
// This is a workaround for a Firefox bug where we don't correctly
|
// This is a workaround for a Firefox bug where we don't correctly
|
||||||
// detect coarse VS fine pointer. For now, let's assume that you have a fine
|
// detect coarse VS fine pointer. For now, let's assume that you have a fine
|
||||||
// pointer if you're on Firefox on desktop.
|
// pointer if you're on Firefox on desktop.
|
||||||
if (
|
const isForcedFinePointer =
|
||||||
editor.environment.isFirefox &&
|
editor.environment.isFirefox && !editor.environment.isAndroid && !editor.environment.isIos
|
||||||
!editor.environment.isAndroid &&
|
|
||||||
!editor.environment.isIos
|
const handleMediaQueryChange = () => {
|
||||||
) {
|
const next = isForcedFinePointer ? false : mql.matches // get the value from the media query
|
||||||
editor.updateInstanceState({ isCoarsePointer: false })
|
if (isCoarse !== next) return // bail if the value hasn't changed
|
||||||
return
|
isCoarse = next // update the local value
|
||||||
|
editor.updateInstanceState({ isCoarsePointer: next }) // update the value in state
|
||||||
}
|
}
|
||||||
if (window.matchMedia) {
|
|
||||||
const mql = window.matchMedia('(pointer: coarse)')
|
if (mql) {
|
||||||
const handler = () => {
|
// set up the listener
|
||||||
editor.updateInstanceState({ isCoarsePointer: !!mql.matches })
|
mql.addEventListener('change', handleMediaQueryChange)
|
||||||
}
|
|
||||||
handler()
|
// and run the handler once to set the initial value
|
||||||
|
handleMediaQueryChange()
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener('touchstart', handleTouchStart)
|
||||||
|
window.removeEventListener('mousemove', handleMouseMove)
|
||||||
|
|
||||||
if (mql) {
|
if (mql) {
|
||||||
mql.addEventListener('change', handler)
|
mql.removeEventListener('change', handleMediaQueryChange)
|
||||||
return () => mql.removeEventListener('change', handler)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [editor])
|
}, [editor])
|
||||||
|
|
Loading…
Reference in a new issue