feat: implements basic support for touch devices

This commit is contained in:
Mark Mankarious
2023-10-11 16:06:56 +01:00
parent d551897bda
commit a841504154
7 changed files with 108 additions and 71 deletions

View File

@@ -2,7 +2,6 @@ import React from 'react';
import { Box, useTheme } from '@mui/material';
import { useUiStateStore } from 'src/stores/uiStateStore';
import { useSceneStore } from 'src/stores/sceneStore';
import { SizeIndicator } from './SizeIndicator';
import { LineItem } from './LineItem';
export const DebugUtils = () => {
@@ -21,63 +20,45 @@ export const DebugUtils = () => {
return (
<Box
sx={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
pointerEvents: 'none'
bgcolor: 'common.white',
px: 2,
py: 1
}}
>
<SizeIndicator />
<Box
sx={{
position: 'absolute',
left: appPadding.x,
bottom: appPadding.y,
bgcolor: 'common.white',
width: 350,
borderRadius: 1,
border: (theme) => {
return `1px solid ${theme.palette.grey[400]}`;
},
px: 2,
py: 1
}}
>
<LineItem
title="Mouse"
value={`${uiState.mouse.position.tile.x}, ${uiState.mouse.position.tile.y}`}
/>
<LineItem
title="Mouse down"
value={
uiState.mouse.mousedown
? `${uiState.mouse.mousedown.tile.x}, ${uiState.mouse.mousedown.tile.y}`
: 'null'
}
/>
<LineItem
title="Mouse delta"
value={
uiState.mouse.delta
? `${uiState.mouse.delta.tile.x}, ${uiState.mouse.delta.tile.y}`
: 'null'
}
/>
<LineItem
title="Scroll"
value={`${uiState.scroll.position.x}, ${uiState.scroll.position.y}`}
/>
<LineItem title="Zoom" value={uiState.zoom} />
<LineItem
title="Size"
value={`${uiState.rendererSize.width}, ${uiState.rendererSize.height}`}
/>
<LineItem title="Scene info" value={`${scene.nodes.length} nodes`} />
<LineItem title="Mode" value={uiState.mode.type} />
<LineItem title="Mode data" value={JSON.stringify(uiState.mode)} />
</Box>
<LineItem
title="Mouse"
value={`${uiState.mouse.position.tile.x}, ${uiState.mouse.position.tile.y}`}
/>
<LineItem
title="Mouse down"
value={
uiState.mouse.mousedown
? `${uiState.mouse.mousedown.tile.x}, ${uiState.mouse.mousedown.tile.y}`
: 'null'
}
/>
<LineItem
title="Mouse delta"
value={
uiState.mouse.delta
? `${uiState.mouse.delta.tile.x}, ${uiState.mouse.delta.tile.y}`
: 'null'
}
/>
<LineItem
title="Scroll"
value={`${uiState.scroll.position.x}, ${uiState.scroll.position.y}`}
/>
<LineItem title="Zoom" value={uiState.zoom} />
<LineItem
title="Size"
value={`${uiState.rendererSize.width}, ${uiState.rendererSize.height}`}
/>
<LineItem title="Scene info" value={`${scene.nodes.length} nodes`} />
<LineItem title="Mode" value={uiState.mode.type} />
<LineItem title="Mode data" value={JSON.stringify(uiState.mode)} />
</Box>
);
};

View File

@@ -9,7 +9,7 @@ import { Rectangles } from 'src/components/SceneLayers/Rectangles/Rectangles';
import { Connectors } from 'src/components/SceneLayers/Connectors/Connectors';
import { ConnectorLabels } from 'src/components/SceneLayers/ConnectorLabels/ConnectorLabels';
import { TextBoxes } from 'src/components/SceneLayers/TextBoxes/TextBoxes';
import { DebugUtils } from 'src/components/DebugUtils/DebugUtils';
import { SizeIndicator } from 'src/components/DebugUtils/SizeIndicator';
import { useResizeObserver } from 'src/hooks/useResizeObserver';
import { SceneLayer } from 'src/components/SceneLayer/SceneLayer';
import { TransformControlsManager } from 'src/components/TransformControlsManager/TransformControlsManager';
@@ -82,7 +82,7 @@ export const Renderer = () => {
</SceneLayer>
{debugMode && (
<SceneLayer>
<DebugUtils />
<SizeIndicator />
</SceneLayer>
)}
<SceneLayer>

View File

@@ -10,6 +10,7 @@ import { useUiStateStore } from 'src/stores/uiStateStore';
import { MainMenu } from 'src/components/MainMenu/MainMenu';
import { ZoomControls } from 'src/components/ZoomControls/ZoomControls';
import { useSceneStore } from 'src/stores/sceneStore';
import { DebugUtils } from 'src/components/DebugUtils/DebugUtils';
export const UiOverlay = () => {
const theme = useTheme();
@@ -23,6 +24,9 @@ export const UiOverlay = () => {
const disableInteractions = useUiStateStore((state) => {
return state.disableInteractions;
});
const debugMode = useUiStateStore((state) => {
return state.debugMode;
});
const mode = useUiStateStore((state) => {
return state.mode;
});
@@ -114,6 +118,21 @@ export const UiOverlay = () => {
{sceneTitle}
</Typography>
</UiElement>
{debugMode && (
<UiElement
sx={{
position: 'absolute',
width: 350,
height: 400,
maxWidth: '100%',
left: appPadding.x,
bottom: appPadding.y * 2 + spacing(2)
}}
>
<DebugUtils />
</UiElement>
)}
</>
);
};

View File

@@ -2,13 +2,19 @@
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=0" />
<link
href="https://fonts.googleapis.com/css2?family=Noto+Sans:wght@200;600&display=swap"
rel="stylesheet"
/>
<title>Development | Isoflow</title>
</head>
<style>
body {
overscroll-behavior: none;
position: fixed;
}
</style>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>

View File

@@ -1,7 +1,7 @@
import { useCallback, useEffect, useRef } from 'react';
import { useSceneStore } from 'src/stores/sceneStore';
import { useUiStateStore } from 'src/stores/uiStateStore';
import { ModeActions, State } from 'src/types';
import { ModeActions, State, SlimMouseEvent } from 'src/types';
import { getMouse } from 'src/utils';
import { Cursor } from './modes/Cursor';
import { DragItems } from './modes/DragItems';
@@ -35,20 +35,17 @@ export const useInteractionManager = () => {
});
const onMouseEvent = useCallback(
(e: MouseEvent | TouchEvent) => {
(e: SlimMouseEvent) => {
if (!rendererRef.current || uiState.disableInteractions) return;
const mode = modes[uiState.mode.type];
const getModeFunction = () => {
switch (e.type) {
case 'touchmove':
case 'mousemove':
return mode.mousemove;
case 'touchstart':
case 'mousedown':
return mode.mousedown;
case 'touchend':
case 'mouseup':
return mode.mouseup;
default:
@@ -101,16 +98,49 @@ export const useInteractionManager = () => {
useEffect(() => {
const el = window;
const onTouchStart = (e: TouchEvent) => {
onMouseEvent({
...e,
clientX: e.touches[0].clientX,
clientY: e.touches[0].clientY,
type: 'mousedown'
});
};
const onTouchMove = (e: TouchEvent) => {
onMouseEvent({
...e,
clientX: e.touches[0].clientX,
clientY: e.touches[0].clientY,
type: 'mousemove'
});
};
const onTouchEnd = (e: TouchEvent) => {
onMouseEvent({
...e,
clientX: uiState.mouse.position.screen.x,
clientY: uiState.mouse.position.screen.y,
type: 'mouseup'
});
};
el.addEventListener('mousemove', onMouseEvent);
el.addEventListener('mousedown', onMouseEvent);
el.addEventListener('mouseup', onMouseEvent);
el.addEventListener('touchstart', onTouchStart);
el.addEventListener('touchmove', onTouchMove);
el.addEventListener('touchend', onTouchEnd);
return () => {
el.removeEventListener('mousemove', onMouseEvent);
el.removeEventListener('mousedown', onMouseEvent);
el.removeEventListener('mouseup', onMouseEvent);
el.removeEventListener('touchstart', onTouchStart);
el.removeEventListener('touchmove', onTouchMove);
el.removeEventListener('touchend', onTouchEnd);
};
}, [onMouseEvent]);
}, [onMouseEvent, uiState.mouse.position.screen]);
const setElement = useCallback((element: HTMLElement) => {
rendererRef.current = element;

View File

@@ -25,3 +25,8 @@ export const ProjectionOrientationEnum = {
} as const;
export type BoundingBox = [Coords, Coords, Coords, Coords];
export type SlimMouseEvent = Pick<
MouseEvent,
'clientX' | 'clientY' | 'target' | 'type'
>;

View File

@@ -23,7 +23,8 @@ import {
ProjectionOrientationEnum,
AnchorPositionsEnum,
BoundingBox,
TextBox
TextBox,
SlimMouseEvent
} from 'src/types';
import {
CoordsUtils,
@@ -250,7 +251,7 @@ interface GetMouse {
zoom: number;
scroll: Scroll;
lastMouse: Mouse;
mouseEvent: MouseEvent | TouchEvent;
mouseEvent: SlimMouseEvent;
rendererSize: Size;
}
@@ -268,12 +269,7 @@ export const getMouse = ({
y: componentOffset?.top ?? 0
};
const clientX =
(mouseEvent as MouseEvent).clientX ??
(mouseEvent as TouchEvent).touches[0].clientX;
const clientY =
(mouseEvent as MouseEvent).clientY ??
(mouseEvent as TouchEvent).touches[0].clientY;
const { clientX, clientY } = mouseEvent;
const mousePosition = {
x: clientX - offset.x,