Improves mobile touch events
This commit is contained in:
parent
1b9e8857e0
commit
6ff7b0c50d
6 changed files with 81 additions and 44 deletions
|
@ -2,13 +2,15 @@ import React, { useRef, memo, useEffect } from 'react'
|
||||||
import state, { useSelector } from 'state'
|
import state, { useSelector } from 'state'
|
||||||
import styled from 'styles'
|
import styled from 'styles'
|
||||||
import { getShapeUtils } from 'lib/shape-utils'
|
import { getShapeUtils } from 'lib/shape-utils'
|
||||||
import { getBoundsCenter, getPage } from 'utils/utils'
|
import { getBoundsCenter, getPage, isMobile } from 'utils/utils'
|
||||||
import { ShapeStyles, ShapeType } from 'types'
|
import { ShapeStyles, ShapeType } from 'types'
|
||||||
import useShapeEvents from 'hooks/useShapeEvents'
|
import useShapeEvents from 'hooks/useShapeEvents'
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
import { getShapeStyle } from 'lib/shape-styles'
|
import { getShapeStyle } from 'lib/shape-styles'
|
||||||
import ContextMenu from 'components/canvas/context-menu/context-menu'
|
import ContextMenu from 'components/canvas/context-menu/context-menu'
|
||||||
|
|
||||||
|
const isMobileDevice = isMobile()
|
||||||
|
|
||||||
interface ShapeProps {
|
interface ShapeProps {
|
||||||
id: string
|
id: string
|
||||||
isSelecting: boolean
|
isSelecting: boolean
|
||||||
|
@ -57,17 +59,20 @@ function Shape({ id, isSelecting, parentPoint }: ShapeProps) {
|
||||||
`
|
`
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledGroup ref={rGroup} transform={transform}>
|
<StyledGroup
|
||||||
{isSelecting &&
|
ref={rGroup}
|
||||||
!isShy &&
|
transform={transform}
|
||||||
(isForeignObject ? (
|
device={isMobileDevice ? 'mobile' : 'desktop'}
|
||||||
|
>
|
||||||
|
{isSelecting && !isShy && (
|
||||||
|
<>
|
||||||
|
{isForeignObject ? (
|
||||||
<HoverIndicator
|
<HoverIndicator
|
||||||
as="rect"
|
as="rect"
|
||||||
width={bounds.width}
|
width={bounds.width}
|
||||||
height={bounds.height}
|
height={bounds.height}
|
||||||
strokeWidth={1.5}
|
strokeWidth={1.5}
|
||||||
variant={'ghost'}
|
variant={'ghost'}
|
||||||
onDoubleClick={() => console.log('aux')}
|
|
||||||
{...events}
|
{...events}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
@ -78,7 +83,9 @@ function Shape({ id, isSelecting, parentPoint }: ShapeProps) {
|
||||||
variant={getShapeUtils(shape).canStyleFill ? 'filled' : 'hollow'}
|
variant={getShapeUtils(shape).canStyleFill ? 'filled' : 'hollow'}
|
||||||
{...events}
|
{...events}
|
||||||
/>
|
/>
|
||||||
))}
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{!shape.isHidden &&
|
{!shape.isHidden &&
|
||||||
(isForeignObject ? (
|
(isForeignObject ? (
|
||||||
|
@ -157,13 +164,11 @@ const StyledGroup = styled('g', {
|
||||||
[`& ${HoverIndicator}`]: {
|
[`& ${HoverIndicator}`]: {
|
||||||
opacity: '0',
|
opacity: '0',
|
||||||
},
|
},
|
||||||
[`&:hover ${HoverIndicator}`]: {
|
|
||||||
opacity: '0.16',
|
|
||||||
},
|
|
||||||
[`&:hover *[data-shy="true"]`]: {
|
|
||||||
opacity: '1',
|
|
||||||
},
|
|
||||||
variants: {
|
variants: {
|
||||||
|
device: {
|
||||||
|
mobile: {},
|
||||||
|
desktop: {},
|
||||||
|
},
|
||||||
isSelected: {
|
isSelected: {
|
||||||
true: {
|
true: {
|
||||||
[`& *[data-shy="true"]`]: {
|
[`& *[data-shy="true"]`]: {
|
||||||
|
@ -172,12 +177,6 @@ const StyledGroup = styled('g', {
|
||||||
[`& ${HoverIndicator}`]: {
|
[`& ${HoverIndicator}`]: {
|
||||||
opacity: '0.2',
|
opacity: '0.2',
|
||||||
},
|
},
|
||||||
[`&:hover ${HoverIndicator}`]: {
|
|
||||||
opacity: '0.3',
|
|
||||||
},
|
|
||||||
[`&:active ${HoverIndicator}`]: {
|
|
||||||
opacity: '0.3',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
false: {
|
false: {
|
||||||
[`& ${HoverIndicator}`]: {
|
[`& ${HoverIndicator}`]: {
|
||||||
|
@ -186,6 +185,32 @@ const StyledGroup = styled('g', {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
compoundVariants: [
|
||||||
|
{
|
||||||
|
device: 'desktop',
|
||||||
|
isSelected: 'false',
|
||||||
|
css: {
|
||||||
|
[`&:hover ${HoverIndicator}`]: {
|
||||||
|
opacity: '0.16',
|
||||||
|
},
|
||||||
|
[`&:hover *[data-shy="true"]`]: {
|
||||||
|
opacity: '1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
device: 'desktop',
|
||||||
|
isSelected: 'true',
|
||||||
|
css: {
|
||||||
|
[`&:hover ${HoverIndicator}`]: {
|
||||||
|
opacity: '0.3',
|
||||||
|
},
|
||||||
|
[`&:active ${HoverIndicator}`]: {
|
||||||
|
opacity: '0.3',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
function Label({ children }: { children: React.ReactNode }) {
|
function Label({ children }: { children: React.ReactNode }) {
|
||||||
|
|
|
@ -42,6 +42,7 @@ export default function useShapeEvents(
|
||||||
const handlePointerEnter = useCallback(
|
const handlePointerEnter = useCallback(
|
||||||
(e: React.PointerEvent) => {
|
(e: React.PointerEvent) => {
|
||||||
if (!inputs.canAccept(e.pointerId)) return
|
if (!inputs.canAccept(e.pointerId)) return
|
||||||
|
|
||||||
if (isParent) {
|
if (isParent) {
|
||||||
state.send('HOVERED_GROUP', inputs.pointerEnter(e, id))
|
state.send('HOVERED_GROUP', inputs.pointerEnter(e, id))
|
||||||
} else {
|
} else {
|
||||||
|
@ -67,6 +68,7 @@ export default function useShapeEvents(
|
||||||
const handlePointerLeave = useCallback(
|
const handlePointerLeave = useCallback(
|
||||||
(e: React.PointerEvent) => {
|
(e: React.PointerEvent) => {
|
||||||
if (!inputs.canAccept(e.pointerId)) return
|
if (!inputs.canAccept(e.pointerId)) return
|
||||||
|
|
||||||
if (isParent) {
|
if (isParent) {
|
||||||
state.send('UNHOVERED_GROUP', { target: id })
|
state.send('UNHOVERED_GROUP', { target: id })
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
import React, { useEffect, useRef } from 'react'
|
import { useRef } from 'react'
|
||||||
import state from 'state'
|
import state from 'state'
|
||||||
import inputs from 'state/inputs'
|
import inputs from 'state/inputs'
|
||||||
import vec from 'utils/vec'
|
import vec from 'utils/vec'
|
||||||
import { useGesture } from 'react-use-gesture'
|
import { useGesture } from 'react-use-gesture'
|
||||||
import {
|
import { fastPanUpdate, fastPinchCamera, fastZoomUpdate } from 'state/hacks'
|
||||||
fastBrushSelect,
|
|
||||||
fastPanUpdate,
|
|
||||||
fastPinchCamera,
|
|
||||||
fastZoomUpdate,
|
|
||||||
} from 'state/hacks'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Capture zoom gestures (pinches, wheels and pans) and send to the state.
|
* Capture zoom gestures (pinches, wheels and pans) and send to the state.
|
||||||
|
|
|
@ -75,7 +75,7 @@ export function fastPinchCamera(
|
||||||
|
|
||||||
camera.point = vec.sub(camera.point, vec.div(delta, camera.zoom))
|
camera.point = vec.sub(camera.point, vec.div(delta, camera.zoom))
|
||||||
|
|
||||||
const next = camera.zoom - (distanceDelta / 300) * camera.zoom
|
const next = camera.zoom - (distanceDelta / 350) * camera.zoom
|
||||||
|
|
||||||
const p0 = screenToWorld(point, data)
|
const p0 = screenToWorld(point, data)
|
||||||
camera.zoom = getCameraZoom(next)
|
camera.zoom = getCameraZoom(next)
|
||||||
|
|
|
@ -146,6 +146,7 @@ class Inputs {
|
||||||
}
|
}
|
||||||
|
|
||||||
delete this.points[e.pointerId]
|
delete this.points[e.pointerId]
|
||||||
|
|
||||||
delete this.activePointerId
|
delete this.activePointerId
|
||||||
|
|
||||||
if (vec.dist(info.origin, info.point) < 8) {
|
if (vec.dist(info.origin, info.point) < 8) {
|
||||||
|
@ -162,6 +163,7 @@ class Inputs {
|
||||||
}
|
}
|
||||||
|
|
||||||
canAccept = (pointerId: PointerEvent['pointerId']) => {
|
canAccept = (pointerId: PointerEvent['pointerId']) => {
|
||||||
|
// return true
|
||||||
return (
|
return (
|
||||||
this.activePointerId === undefined || this.activePointerId === pointerId
|
this.activePointerId === undefined || this.activePointerId === pointerId
|
||||||
)
|
)
|
||||||
|
@ -177,6 +179,12 @@ class Inputs {
|
||||||
vec.dist(origin, point) < 8
|
vec.dist(origin, point) < 8
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.activePointerId = undefined
|
||||||
|
this.pointer = undefined
|
||||||
|
this.points = {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new Inputs()
|
export default new Inputs()
|
||||||
|
|
|
@ -176,7 +176,7 @@ const state = createState({
|
||||||
initial: 'selecting',
|
initial: 'selecting',
|
||||||
states: {
|
states: {
|
||||||
selecting: {
|
selecting: {
|
||||||
onEnter: 'setActiveToolSelect',
|
onEnter: ['setActiveToolSelect', 'clearInputs'],
|
||||||
on: {
|
on: {
|
||||||
UNDO: 'undo',
|
UNDO: 'undo',
|
||||||
REDO: 'redo',
|
REDO: 'redo',
|
||||||
|
@ -391,6 +391,7 @@ const state = createState({
|
||||||
onEnter: 'startTranslateSession',
|
onEnter: 'startTranslateSession',
|
||||||
onExit: 'completeSession',
|
onExit: 'completeSession',
|
||||||
on: {
|
on: {
|
||||||
|
STARTED_PINCHING: { to: 'pinching' },
|
||||||
MOVED_POINTER: 'updateTranslateSession',
|
MOVED_POINTER: 'updateTranslateSession',
|
||||||
PANNED_CAMERA: 'updateTranslateSession',
|
PANNED_CAMERA: 'updateTranslateSession',
|
||||||
PRESSED_SHIFT_KEY: 'keyUpdateTranslateSession',
|
PRESSED_SHIFT_KEY: 'keyUpdateTranslateSession',
|
||||||
|
@ -1248,6 +1249,10 @@ const state = createState({
|
||||||
|
|
||||||
/* -------------------- Selection ------------------- */
|
/* -------------------- Selection ------------------- */
|
||||||
|
|
||||||
|
clearInputs() {
|
||||||
|
inputs.clear()
|
||||||
|
},
|
||||||
|
|
||||||
selectAll(data) {
|
selectAll(data) {
|
||||||
const selectedIds = getSelectedIds(data)
|
const selectedIds = getSelectedIds(data)
|
||||||
const page = getPage(data)
|
const page = getPage(data)
|
||||||
|
@ -1512,6 +1517,8 @@ const state = createState({
|
||||||
point: number[]
|
point: number[]
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
// This is usually replaced with hacks.fastPinchCamera!
|
||||||
|
|
||||||
const camera = getCurrentCamera(data)
|
const camera = getCurrentCamera(data)
|
||||||
camera.point = vec.sub(camera.point, vec.div(payload.delta, camera.zoom))
|
camera.point = vec.sub(camera.point, vec.div(payload.delta, camera.zoom))
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue