Skip to content

Commit

Permalink
feat(app-board): add zoom in/out buttons
Browse files Browse the repository at this point in the history
  • Loading branch information
rams23 committed Jan 26, 2021
1 parent 116b968 commit 06c8d99
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 8 deletions.
12 changes: 10 additions & 2 deletions packages/game-app/src/_shared/components/FabDial/FabDial.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useMemo, useState } from 'react';
import IconButton from '../IconButton';
import styled, { css } from 'styled-components';

Expand All @@ -9,6 +9,7 @@ type Props = {
buttons: {
icon: React.ReactNode;
onClick: () => void;
autoClose?: boolean;
}[];
};

Expand Down Expand Up @@ -53,14 +54,21 @@ const MainButtonIcon = styled(IconButton)`
const FabDial: React.FC<Props> = ({ icon, buttons, className }) => {
const [open, setOpen] = useState(false);

const callbacks = useMemo(() => {
return buttons.map(b => () => {
b.onClick();
b.autoClose && setOpen(false);
});
}, [buttons]);

return (
<div style={{ position: 'relative', width: '40px', height: '40px' }} className={className}>
<MainButtonIcon onClick={() => setOpen(s => !s)} variant="rounded">
{icon}
</MainButtonIcon>
<ButtonsWrapper isOpen={open}>
{buttons.map((b, index) => (
<IconButton key={index} variant="rounded" onClick={b.onClick}>
<IconButton key={index} variant="rounded" onClick={callbacks[index]}>
{b.icon}
</IconButton>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type Props = {};
const BottomWidgetsRowStyled: React.FC<Props> = () => {
const state = useSelector(selectors.getCardStateForUI);

const { setScaleAndPanRef } = useZoomPanRefs();
const { setScaleAndPanRef, zoomRef } = useZoomPanRefs();

const fitWindow = useCallback(() => {
const { actualScale, pan } = calculatePanAndZoomToFitWindow(state);
Expand All @@ -27,6 +27,14 @@ const BottomWidgetsRowStyled: React.FC<Props> = () => {
});
}, [setScaleAndPanRef, state]);

const zoomIn = useCallback(() => {
zoomRef.current?.(1.1);
}, [zoomRef]);

const zoomOut = useCallback(() => {
zoomRef.current?.(0.9);
}, [zoomRef]);

return (
<BottomWidgetsRowContainer>
<ScenarioPanel />
Expand All @@ -43,15 +51,16 @@ const BottomWidgetsRowStyled: React.FC<Props> = () => {
buttons={[
{
icon: <ZoomInIcon />,
onClick: () => ({}),
onClick: zoomIn,
},
{
icon: <ZoomOutIcon />,
onClick: () => ({}),
onClick: zoomOut,
},
{
icon: <FitScreenIcon />,
onClick: fitWindow,
autoClose: true,
},
]}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { containerStyle, contentStyle } from './ZoomPanContainer.style';

type Props = React.PropsWithChildren<{}>;

const scaleFactor = 0.99;
const scaleFactor = 0.9;
const boardSize = { width: 3840, height: 2160 };

const minScale = Math.max(window.innerWidth / boardSize.width, window.innerHeight / boardSize.height);
Expand All @@ -32,7 +32,12 @@ function constrainPan(pan: Pan, scale: number): Pan {
* Compute new pan to keep the cursor position fixed and scale centered
* at that specific point
*/
function calculateNewPan(newScale: number, currentScale: number, ev: WheelEvent, currentPan: Pan) {
function calculateNewPan(
newScale: number,
currentScale: number,
ev: { pageX: number; pageY: number },
currentPan: Pan,
) {
const recalculatedFactor = newScale / currentScale;

const mouseWindowX = ev.pageX;
Expand All @@ -54,7 +59,7 @@ const ZoomPanContainer: React.FC<Props> = ({ children }) => {
// pan is at board dimension, so scaled
const isPanning = useRef(false);

const { panRef, scaleRef, setScaleAndPanRef, setZoomAndPan: setValuesInContext } = useZoomPanSetters();
const { panRef, scaleRef, setScaleAndPanRef, setZoomAndPan: setValuesInContext, zoomRef } = useZoomPanSetters();

const setDivTransformation = useCallback(
(scale: number, pan: Pan) => {
Expand All @@ -79,6 +84,24 @@ const ZoomPanContainer: React.FC<Props> = ({ children }) => {
[panRef, scaleRef, setDivTransformation],
);

const zoom = useCallback(
(factor: number) => {
const newScale = Math.max(scaleRef.current * factor, minScale);
const newPan = calculateNewPan(
newScale,
scaleRef.current,
{
pageX: window.innerWidth / 2,
pageY: window.innerHeight / 2,
},
panRef.current,
);

setZoomAndPanInternal({ pan: newPan, scale: newScale });
},
[panRef, scaleRef, setZoomAndPanInternal],
);

const onPointerMove = useCallback(
(ev: React.PointerEvent) => {
if (isPanning.current) {
Expand Down Expand Up @@ -136,6 +159,10 @@ const ZoomPanContainer: React.FC<Props> = ({ children }) => {
setScaleAndPanRef.current = setZoomAndPanInternal;
}, [setZoomAndPanInternal, setScaleAndPanRef]);

useEffect(() => {
zoomRef.current = zoom;
}, [zoom, zoomRef]);

const setRef = useCallback(
(ref: HTMLDivElement) => {
if (ref) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export type ZoomPanRefs = {
scaleRef: React.MutableRefObject<number>;
panRef: React.MutableRefObject<Pan>;
setScaleAndPanRef: React.MutableRefObject<(props: { scale?: number; pan?: Pan }) => void>;
zoomRef: React.MutableRefObject<(factor: number) => void>;
};

export type ZoomPanSetter = {
Expand Down Expand Up @@ -43,12 +44,14 @@ const ZoomPan: React.FC<Props> = ({ children, initialScale, initialPan }) => {
const scaleRef = useRef(initialScale || 1);
const panRef = useRef(initialPan || { x: 0, y: 0 });
const setScaleAndPanRef = useRef((props: { scale?: number; pan?: Pan }) => ({}));
const zoomRef = useRef((factor: number) => {});

const refsState = useMemo(() => {
return {
panRef: panRef,
setScaleAndPanRef: setScaleAndPanRef,
scaleRef: scaleRef,
zoomRef: zoomRef,
} as ZoomPanRefs;
}, []);

Expand All @@ -58,6 +61,7 @@ const ZoomPan: React.FC<Props> = ({ children, initialScale, initialPan }) => {
setScaleAndPanRef: setScaleAndPanRef,
scaleRef: scaleRef,
setZoomAndPan: setZoomAndPan,
zoomRef: zoomRef,
} as ZoomPanSetter;
}, [setZoomAndPan]);

Expand Down

0 comments on commit 06c8d99

Please sign in to comment.