Skip to content

Commit

Permalink
Merge pull request viveknigam3003#23 from akram-r/main
Browse files Browse the repository at this point in the history
Snapping fixes
  • Loading branch information
viveknigam3003 authored Dec 20, 2023
2 parents 9846285 + 6f78ffc commit 8a1110a
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 55 deletions.
57 changes: 38 additions & 19 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,9 @@ function App() {
const dispatch = useDispatch();
const artboards = useSelector((state: RootState) => state.app.artboards);
const selectedArtboard = useSelector((state: RootState) => state.app.selectedArtboard);
const [snapDistance] = useLocalStorage<string>({
const [snapDistance, setSnapDistance] = useLocalStorage<string>({
key: 'snapDistance',
defaultValue: '2',
getInitialValueInEffect: true,
});
const theme = useMantineTheme();
const { classes } = useStyles();
Expand All @@ -63,6 +62,7 @@ function App() {
const { classes: modalClasses } = useModalStyles();
const [opened, { open, close }] = useDisclosure();
const [zoomLevel, setZoomLevel] = useState(1);
const [canvasScrollPoints, setCanvasScrollPoints] = useState(0);
const newArtboardForm = useForm<Omit<Artboard, 'id'> & { number: number }>({
initialValues: {
name: '',
Expand Down Expand Up @@ -143,26 +143,39 @@ function App() {
setCurrentSelectedElements(null);
});

canvasRef.current.on('object:moving', function (options) {
const target = options.target as fabric.Object;
snapToObject(
target as snappingObjectType,
filterSnappingLines(canvasRef.current?.getObjects()) as snappingObjectType[],
guidesRef,
canvasRef,
Number(snapDistance),
);
});
canvasRef.current.on('object:modified', function () {
Object.entries(guidesRef.current).forEach(([, value]) => {
value?.set({ opacity: 0 });
});
});
return () => {
canvasRef.current?.dispose();
};
}, []);

const onMoveHandler = (options: fabric.IEvent) => {
const target = options.target as fabric.Object;
snapToObject(
target as snappingObjectType,
filterSnappingLines(canvasRef.current?.getObjects()) as snappingObjectType[],
guidesRef,
canvasRef,
Number(snapDistance),
);
};

const onModifiedHandler = () => {
Object.entries(guidesRef.current).forEach(([, value]) => {
value?.set({ opacity: 0 });
});
};

useEffect(() => {
if (canvasRef.current) {
canvasRef.current.on('object:moving', onMoveHandler);
canvasRef.current.on('object:modified', onModifiedHandler);
}
return () => {
canvasRef.current?.off('object:moving', onMoveHandler);
canvasRef.current?.off('object:modified', onModifiedHandler);
};
}, [canvasRef.current, snapDistance]);

useEffect(() => {
dispatch(updateActiveArtboardLayers(selectedArtboard?.state?.objects || []));
}, [selectedArtboard, dispatch]);
Expand Down Expand Up @@ -688,7 +701,7 @@ function App() {
}
});

guidesRef.current = createSnappingLines(canvasRef, artboardRef);
guidesRef.current = createSnappingLines(canvasRef);
canvas.requestRenderAll();
});
}, [selectedArtboard, artboards]);
Expand Down Expand Up @@ -726,9 +739,9 @@ function App() {
if (!vpt) {
return;
}

vpt[4] -= e.deltaX;
vpt[5] -= e.deltaY;
setCanvasScrollPoints(vpt[4] + vpt[5]);
canvas.requestRenderAll();
}
};
Expand Down Expand Up @@ -756,6 +769,10 @@ function App() {
};
}, []);

// this is hack to reset snapping lines when zoom level changes or scroll changes,ideal solution will be move this to handlePan function and change the snapping lines based on the scroll and zoom level
useEffect(() => {
guidesRef.current = createSnappingLines(canvasRef);
}, [zoomLevel, canvasScrollPoints]);
useEffect(() => {
if (!autosaveChanges) {
return;
Expand Down Expand Up @@ -895,6 +912,8 @@ function App() {
setShowSidebar={setShowSidebar}
autosaveChanges={autosaveChanges}
setAutoSaveChanges={setAutoSaveChanges}
snapDistance={snapDistance}
setSnapDistance={setSnapDistance}
/>
<Tooltip label="Save" openDelay={500}>
<ActionIcon onClick={saveArtboardChanges} size={20}>
Expand Down
11 changes: 10 additions & 1 deletion src/modules/settings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ interface Props {
setShowSidebar: React.Dispatch<React.SetStateAction<boolean>>;
autosaveChanges: boolean;
setAutoSaveChanges: React.Dispatch<React.SetStateAction<boolean>>;
snapDistance: string;
setSnapDistance: React.Dispatch<React.SetStateAction<string>>;
}

const SettingsMenu: React.FC<Props> = ({
Expand All @@ -29,6 +31,8 @@ const SettingsMenu: React.FC<Props> = ({
setShowSidebar,
autosaveChanges,
setAutoSaveChanges,
snapDistance,
setSnapDistance,
}) => {
const { classes } = useMenuStyles();
const { colorScheme, toggleColorScheme } = useMantineColorScheme();
Expand Down Expand Up @@ -142,7 +146,12 @@ const SettingsMenu: React.FC<Props> = ({
onClose={closeColorSpaceModal}
recreateCanvas={recreateCanvas}
/>
<SnapDistanceModal open={snapDistanceModalOpened} onClose={closeSnapDistanceModal} />
<SnapDistanceModal
open={snapDistanceModalOpened}
onClose={closeSnapDistanceModal}
snapDistance={snapDistance}
setSnapDistance={setSnapDistance}
/>
</Box>
);
};
Expand Down
18 changes: 9 additions & 9 deletions src/modules/snapping/SnapDistanceModal.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { Button, Modal, NumberInput, Stack } from '@mantine/core';

import { useModalStyles } from '../../styles/modal';
import { useForm } from '@mantine/form';
import { useLocalStorage } from '@mantine/hooks';
import { useEffect } from 'react';

const SnapDistanceModal = ({ open, onClose }: any) => {
const [snapDistance, setSnapDistance] = useLocalStorage<string>({
key: 'snapDistance',
defaultValue: '2',
getInitialValueInEffect: true,
});
type SnapDistanceModalType = {
open: boolean;
onClose: () => void;
snapDistance: string;
setSnapDistance: React.Dispatch<React.SetStateAction<string>>;
};

const SnapDistanceModal = ({ open, onClose, snapDistance, setSnapDistance }: SnapDistanceModalType) => {
const { classes: modalClasses } = useModalStyles();
const imageForm = useForm<{ snapDistance: number }>({
validate: values => {
const errors: Record<string, string> = {};
if (!(values.snapDistance > 1 && values.snapDistance <= 10)) {
if (!(values.snapDistance > 1 && values.snapDistance < 11)) {
errors.snapDistance = 'Snap distance should be between 2 and 10';
}
return errors;
Expand Down
58 changes: 32 additions & 26 deletions src/modules/snapping/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,34 @@ export function snapToObject(
} else {
guidesRef?.current?.centerY?.set({ opacity: 0 });
}
guidesRef?.current?.left?.setCoords();
guidesRef?.current?.right?.setCoords();
guidesRef?.current?.top?.setCoords();
guidesRef?.current?.bottom?.setCoords();
guidesRef?.current?.centerX?.setCoords();
guidesRef?.current?.centerY?.setCoords();
canvasRef.current?.renderAll();
});
}

export function createSnappingLines(
canvasRef: React.MutableRefObject<fabric.Canvas | null>,
artboardRef: React.MutableRefObject<fabric.Rect | null>,
) {
const artboardWidth = artboardRef.current?.width as number;
const artboardHeight = artboardRef.current?.height as number;
const getVisibleTopLeft = (canvasRef: React.MutableRefObject<fabric.Canvas | null>) => {
const canvas = canvasRef.current as fabric.Canvas;
const vpt = canvas.viewportTransform as unknown as fabric.IPoint[];
const scrollTop = window.scrollY || document.documentElement.scrollTop;
const scrollLeft = window.scrollX || document.documentElement.scrollLeft;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const visibleTop = -vpt[5] / vpt[0] + scrollTop / vpt[0];
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const visibleLeft = -vpt[4] / vpt[0] + scrollLeft / vpt[0];
return { top: visibleTop, left: visibleLeft };
};

export function createSnappingLines(canvasRef: React.MutableRefObject<fabric.Canvas | null>) {
const canvasWidth = (canvasRef.current?.width as number) / (canvasRef.current?.getZoom() as number);
const canvasHeight = (canvasRef.current?.height as number) / (canvasRef.current?.getZoom() as number);
const { top, left } = getVisibleTopLeft(canvasRef);
const defaultSnapLineProps = {
opacity: 0,
evented: false,
Expand All @@ -157,30 +175,18 @@ export function createSnappingLines(
},
};
const guidesRef = {
left: new fabric.Line([0, artboardWidth, 0, 0], {
top: artboardRef?.current?.top,
...defaultSnapLineProps,
}),
top: new fabric.Line([0, 0, artboardHeight, 0], {
left: artboardRef?.current?.left,
...defaultSnapLineProps,
}),
right: new fabric.Line([0, artboardWidth, 0, 0], {
top: artboardRef?.current?.top,
...defaultSnapLineProps,
}),
bottom: new fabric.Line([0, 0, artboardWidth, 0], {
left: artboardRef?.current?.left,
...defaultSnapLineProps,
}),
centerX: new fabric.Line([0, artboardHeight, 0, 0], {
top: artboardRef?.current?.top,
left: new fabric.Line([0, canvasWidth, 0, 0], {
...defaultSnapLineProps,
top,
}),
centerY: new fabric.Line([0, 0, artboardHeight, 0], {
left: artboardRef?.current?.left,
top: new fabric.Line([0, 0, canvasHeight, 0], {
...defaultSnapLineProps,
left,
}),
right: new fabric.Line([0, canvasWidth, 0, 0], { ...defaultSnapLineProps, top }),
bottom: new fabric.Line([0, 0, canvasWidth, 0], { ...defaultSnapLineProps, left }),
centerX: new fabric.Line([0, canvasHeight, 0, 0], { ...defaultSnapLineProps, top }),
centerY: new fabric.Line([0, 0, canvasHeight, 0], { ...defaultSnapLineProps, left }),
};
canvasRef.current
?.getObjects()
Expand Down

0 comments on commit 8a1110a

Please sign in to comment.