fix(ui): fix canvas scaling

This commit is contained in:
psychedelicious 2023-08-23 21:53:20 +10:00
parent 06ac16a77d
commit 6efa953172
3 changed files with 99 additions and 45 deletions

View File

@ -1,6 +1,6 @@
import { Box, chakra, Flex } from '@chakra-ui/react'; import { Box, chakra, Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import { import {
canvasSelector, canvasSelector,
@ -9,7 +9,7 @@ import {
import Konva from 'konva'; import Konva from 'konva';
import { KonvaEventObject } from 'konva/lib/Node'; import { KonvaEventObject } from 'konva/lib/Node';
import { Vector2d } from 'konva/lib/types'; import { Vector2d } from 'konva/lib/types';
import { memo, useCallback, useRef } from 'react'; import { memo, useCallback, useEffect, useRef } from 'react';
import { Layer, Stage } from 'react-konva'; import { Layer, Stage } from 'react-konva';
import useCanvasDragMove from '../hooks/useCanvasDragMove'; import useCanvasDragMove from '../hooks/useCanvasDragMove';
import useCanvasHotkeys from '../hooks/useCanvasHotkeys'; import useCanvasHotkeys from '../hooks/useCanvasHotkeys';
@ -18,6 +18,7 @@ import useCanvasMouseMove from '../hooks/useCanvasMouseMove';
import useCanvasMouseOut from '../hooks/useCanvasMouseOut'; import useCanvasMouseOut from '../hooks/useCanvasMouseOut';
import useCanvasMouseUp from '../hooks/useCanvasMouseUp'; import useCanvasMouseUp from '../hooks/useCanvasMouseUp';
import useCanvasWheel from '../hooks/useCanvasZoom'; import useCanvasWheel from '../hooks/useCanvasZoom';
import { canvasResized } from '../store/canvasSlice';
import { import {
setCanvasBaseLayer, setCanvasBaseLayer,
setCanvasStage, setCanvasStage,
@ -106,7 +107,8 @@ const IAICanvas = () => {
shouldAntialias, shouldAntialias,
} = useAppSelector(selector); } = useAppSelector(selector);
useCanvasHotkeys(); useCanvasHotkeys();
const dispatch = useAppDispatch();
const containerRef = useRef<HTMLDivElement>(null);
const stageRef = useRef<Konva.Stage | null>(null); const stageRef = useRef<Konva.Stage | null>(null);
const canvasBaseLayerRef = useRef<Konva.Layer | null>(null); const canvasBaseLayerRef = useRef<Konva.Layer | null>(null);
@ -137,8 +139,30 @@ const IAICanvas = () => {
const { handleDragStart, handleDragMove, handleDragEnd } = const { handleDragStart, handleDragMove, handleDragEnd } =
useCanvasDragMove(); useCanvasDragMove();
useEffect(() => {
if (!containerRef.current) {
return;
}
const resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
if (entry.contentBoxSize) {
const { width, height } = entry.contentRect;
dispatch(canvasResized({ width, height }));
}
}
});
resizeObserver.observe(containerRef.current);
return () => {
resizeObserver.disconnect();
};
}, [dispatch]);
return ( return (
<Flex <Flex
id="canvas-container"
ref={containerRef}
sx={{ sx={{
position: 'relative', position: 'relative',
height: '100%', height: '100%',
@ -146,13 +170,18 @@ const IAICanvas = () => {
borderRadius: 'base', borderRadius: 'base',
}} }}
> >
<Box sx={{ position: 'relative' }}> <Box
sx={{
position: 'absolute',
// top: 0,
// insetInlineStart: 0,
}}
>
<ChakraStage <ChakraStage
tabIndex={-1} tabIndex={-1}
ref={canvasStageRefCallback} ref={canvasStageRefCallback}
sx={{ sx={{
outline: 'none', outline: 'none',
// boxShadow: '0px 0px 0px 1px var(--border-color-light)',
overflow: 'hidden', overflow: 'hidden',
cursor: stageCursor ? stageCursor : undefined, cursor: stageCursor ? stageCursor : undefined,
canvas: { canvas: {
@ -213,9 +242,9 @@ const IAICanvas = () => {
/> />
</Layer> </Layer>
</ChakraStage> </ChakraStage>
</Box>
<IAICanvasStatusText /> <IAICanvasStatusText />
<IAICanvasStagingAreaToolbar /> <IAICanvasStagingAreaToolbar />
</Box>
</Flex> </Flex>
); );
}; };

View File

@ -623,6 +623,53 @@ export const canvasSlice = createSlice({
} }
} }
}, },
canvasResized: (
state,
action: PayloadAction<{ width: number; height: number }>
) => {
const { width, height } = action.payload;
const newStageDimensions = {
width: Math.floor(width),
height: Math.floor(height),
};
state.stageDimensions = newStageDimensions;
if (!state.layerState.objects.find(isCanvasBaseImage)) {
const newScale = calculateScale(
newStageDimensions.width,
newStageDimensions.height,
512,
512,
STAGE_PADDING_PERCENTAGE
);
const newCoordinates = calculateCoordinates(
newStageDimensions.width,
newStageDimensions.height,
0,
0,
512,
512,
newScale
);
const newBoundingBoxDimensions = { width: 512, height: 512 };
state.stageScale = newScale;
state.stageCoordinates = newCoordinates;
state.boundingBoxCoordinates = { x: 0, y: 0 };
state.boundingBoxDimensions = newBoundingBoxDimensions;
if (state.boundingBoxScaleMethod === 'auto') {
const scaledDimensions = getScaledBoundingBoxDimensions(
newBoundingBoxDimensions
);
state.scaledBoundingBoxDimensions = scaledDimensions;
}
}
},
resetCanvasView: ( resetCanvasView: (
state, state,
action: PayloadAction<{ action: PayloadAction<{
@ -966,6 +1013,7 @@ export const {
stagingAreaInitialized, stagingAreaInitialized,
canvasSessionIdChanged, canvasSessionIdChanged,
setShouldAntialias, setShouldAntialias,
canvasResized,
} = canvasSlice.actions; } = canvasSlice.actions;
export default canvasSlice.reducer; export default canvasSlice.reducer;

View File

@ -1,11 +1,10 @@
import { Box, Flex } from '@chakra-ui/react'; import { Flex } from '@chakra-ui/react';
import { createSelector } from '@reduxjs/toolkit'; import { createSelector } from '@reduxjs/toolkit';
import { stateSelector } from 'app/store/store'; import { stateSelector } from 'app/store/store';
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions';
import IAIDropOverlay from 'common/components/IAIDropOverlay'; import IAIDropOverlay from 'common/components/IAIDropOverlay';
import IAICanvas from 'features/canvas/components/IAICanvas'; import IAICanvas from 'features/canvas/components/IAICanvas';
import IAICanvasResizer from 'features/canvas/components/IAICanvasResizer';
import IAICanvasToolbar from 'features/canvas/components/IAICanvasToolbar/IAICanvasToolbar'; import IAICanvasToolbar from 'features/canvas/components/IAICanvasToolbar/IAICanvasToolbar';
import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale'; import { requestCanvasRescale } from 'features/canvas/store/thunks/requestCanvasScale';
import { useDroppableTypesafe } from 'features/dnd/hooks/typesafeHooks'; import { useDroppableTypesafe } from 'features/dnd/hooks/typesafeHooks';
@ -54,49 +53,27 @@ const UnifiedCanvasContent = () => {
}, [dispatch]); }, [dispatch]);
return ( return (
<Box <Flex
layerStyle="first"
ref={setDroppableRef} ref={setDroppableRef}
tabIndex={-1} tabIndex={-1}
sx={{
layerStyle: 'first',
w: 'full',
h: 'full',
p: 4,
borderRadius: 'base',
}}
>
<Flex
sx={{ sx={{
flexDirection: 'column', flexDirection: 'column',
alignItems: 'center', alignItems: 'center',
gap: 4, gap: 4,
p: 2,
borderRadius: 'base',
w: 'full', w: 'full',
h: 'full', h: 'full',
}} }}
> >
<IAICanvasToolbar /> <IAICanvasToolbar />
<Flex <IAICanvas />
sx={{ {/* {doesCanvasNeedScaling ? <IAICanvasResizer /> : <IAICanvas />} */}
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: 4,
w: 'full',
h: 'full',
}}
>
<Box sx={{ w: 'full', h: 'full', position: 'relative' }}>
{doesCanvasNeedScaling ? <IAICanvasResizer /> : <IAICanvas />}
{isValidDrop(droppableData, active) && ( {isValidDrop(droppableData, active) && (
<IAIDropOverlay <IAIDropOverlay isOver={isOver} label="Set Canvas Initial Image" />
isOver={isOver}
label="Set Canvas Initial Image"
/>
)} )}
</Box>
</Flex> </Flex>
</Flex>
</Box>
); );
}; };