From 6ff7b0c50d638807cefd02b6fc11e832b107357d Mon Sep 17 00:00:00 2001 From: Steve Ruiz Date: Fri, 18 Jun 2021 16:19:10 +0100 Subject: [PATCH] Improves mobile touch events --- components/canvas/shape.tsx | 95 +++++++++++++++++++++++-------------- hooks/useShapeEvents.ts | 2 + hooks/useZoomEvents.ts | 9 +--- state/hacks.ts | 2 +- state/inputs.tsx | 8 ++++ state/state.ts | 9 +++- 6 files changed, 81 insertions(+), 44 deletions(-) diff --git a/components/canvas/shape.tsx b/components/canvas/shape.tsx index 8d4846f2a..483715dc9 100644 --- a/components/canvas/shape.tsx +++ b/components/canvas/shape.tsx @@ -2,13 +2,15 @@ import React, { useRef, memo, useEffect } from 'react' import state, { useSelector } from 'state' import styled from 'styles' 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 useShapeEvents from 'hooks/useShapeEvents' import vec from 'utils/vec' import { getShapeStyle } from 'lib/shape-styles' import ContextMenu from 'components/canvas/context-menu/context-menu' +const isMobileDevice = isMobile() + interface ShapeProps { id: string isSelecting: boolean @@ -57,28 +59,33 @@ function Shape({ id, isSelecting, parentPoint }: ShapeProps) { ` return ( - - {isSelecting && - !isShy && - (isForeignObject ? ( - console.log('aux')} - {...events} - /> - ) : ( - - ))} + + {isSelecting && !isShy && ( + <> + {isForeignObject ? ( + + ) : ( + + )} + + )} {!shape.isHidden && (isForeignObject ? ( @@ -157,13 +164,11 @@ const StyledGroup = styled('g', { [`& ${HoverIndicator}`]: { opacity: '0', }, - [`&:hover ${HoverIndicator}`]: { - opacity: '0.16', - }, - [`&:hover *[data-shy="true"]`]: { - opacity: '1', - }, variants: { + device: { + mobile: {}, + desktop: {}, + }, isSelected: { true: { [`& *[data-shy="true"]`]: { @@ -172,12 +177,6 @@ const StyledGroup = styled('g', { [`& ${HoverIndicator}`]: { opacity: '0.2', }, - [`&:hover ${HoverIndicator}`]: { - opacity: '0.3', - }, - [`&:active ${HoverIndicator}`]: { - opacity: '0.3', - }, }, false: { [`& ${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 }) { diff --git a/hooks/useShapeEvents.ts b/hooks/useShapeEvents.ts index 57d57c022..b21ba0db1 100644 --- a/hooks/useShapeEvents.ts +++ b/hooks/useShapeEvents.ts @@ -42,6 +42,7 @@ export default function useShapeEvents( const handlePointerEnter = useCallback( (e: React.PointerEvent) => { if (!inputs.canAccept(e.pointerId)) return + if (isParent) { state.send('HOVERED_GROUP', inputs.pointerEnter(e, id)) } else { @@ -67,6 +68,7 @@ export default function useShapeEvents( const handlePointerLeave = useCallback( (e: React.PointerEvent) => { if (!inputs.canAccept(e.pointerId)) return + if (isParent) { state.send('UNHOVERED_GROUP', { target: id }) } else { diff --git a/hooks/useZoomEvents.ts b/hooks/useZoomEvents.ts index 2c1a09f5e..22cec4a1c 100644 --- a/hooks/useZoomEvents.ts +++ b/hooks/useZoomEvents.ts @@ -1,14 +1,9 @@ -import React, { useEffect, useRef } from 'react' +import { useRef } from 'react' import state from 'state' import inputs from 'state/inputs' import vec from 'utils/vec' import { useGesture } from 'react-use-gesture' -import { - fastBrushSelect, - fastPanUpdate, - fastPinchCamera, - fastZoomUpdate, -} from 'state/hacks' +import { fastPanUpdate, fastPinchCamera, fastZoomUpdate } from 'state/hacks' /** * Capture zoom gestures (pinches, wheels and pans) and send to the state. diff --git a/state/hacks.ts b/state/hacks.ts index b818baa8a..145076ab2 100644 --- a/state/hacks.ts +++ b/state/hacks.ts @@ -75,7 +75,7 @@ export function fastPinchCamera( 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) camera.zoom = getCameraZoom(next) diff --git a/state/inputs.tsx b/state/inputs.tsx index 2de10a158..eff6dea31 100644 --- a/state/inputs.tsx +++ b/state/inputs.tsx @@ -146,6 +146,7 @@ class Inputs { } delete this.points[e.pointerId] + delete this.activePointerId if (vec.dist(info.origin, info.point) < 8) { @@ -162,6 +163,7 @@ class Inputs { } canAccept = (pointerId: PointerEvent['pointerId']) => { + // return true return ( this.activePointerId === undefined || this.activePointerId === pointerId ) @@ -177,6 +179,12 @@ class Inputs { vec.dist(origin, point) < 8 ) } + + clear() { + this.activePointerId = undefined + this.pointer = undefined + this.points = {} + } } export default new Inputs() diff --git a/state/state.ts b/state/state.ts index 2f46e9223..7beda7e4e 100644 --- a/state/state.ts +++ b/state/state.ts @@ -176,7 +176,7 @@ const state = createState({ initial: 'selecting', states: { selecting: { - onEnter: 'setActiveToolSelect', + onEnter: ['setActiveToolSelect', 'clearInputs'], on: { UNDO: 'undo', REDO: 'redo', @@ -391,6 +391,7 @@ const state = createState({ onEnter: 'startTranslateSession', onExit: 'completeSession', on: { + STARTED_PINCHING: { to: 'pinching' }, MOVED_POINTER: 'updateTranslateSession', PANNED_CAMERA: 'updateTranslateSession', PRESSED_SHIFT_KEY: 'keyUpdateTranslateSession', @@ -1248,6 +1249,10 @@ const state = createState({ /* -------------------- Selection ------------------- */ + clearInputs() { + inputs.clear() + }, + selectAll(data) { const selectedIds = getSelectedIds(data) const page = getPage(data) @@ -1512,6 +1517,8 @@ const state = createState({ point: number[] } ) { + // This is usually replaced with hacks.fastPinchCamera! + const camera = getCurrentCamera(data) camera.point = vec.sub(camera.point, vec.div(payload.delta, camera.zoom))