Skip to content

Commit

Permalink
fix: Made canvas responsive on mobile and some ui responsiveness (#226)
Browse files Browse the repository at this point in the history
* fix: made canvas responsive

* fix: resolved pr comments

* More universal responsiveness query, patch issues with scrolling at bounds of scale, generic expandable tab mobile impl

* Remove unused style

---------

Co-authored-by: Brandon Roberts <[email protected]>
  • Loading branch information
addegbenga and b-j-roberts authored Jun 12, 2024
1 parent db7b058 commit c8b4f8b
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 14 deletions.
8 changes: 6 additions & 2 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import CanvasContainer from './canvas/CanvasContainer.js';
import PixelSelector from './footer/PixelSelector.js';
import TabsFooter from './footer/TabsFooter.js';
import TabPanel from './tabs/TabPanel.js';
import { usePreventZoom } from './utils/Window.js';
import { usePreventZoom, useLockScroll } from './utils/Window.js';
import { backendUrl, wsUrl, devnetMode } from './utils/Consts.js';
import logo from './resources/logo.png';
import canvasConfig from './configs/canvas.config.json';
Expand All @@ -24,6 +24,7 @@ import NotificationPanel from './tabs/NotificationPanel.js';
function App() {
// Window management
usePreventZoom();
useLockScroll();

const isDesktopOrLaptop = useMediaQuery({
query: '(min-width: 1224px)'
Expand All @@ -32,6 +33,7 @@ function App() {
const isTabletOrMobile = useMediaQuery({ query: '(max-width: 1224px)' });
const isPortrait = useMediaQuery({ query: '(orientation: portrait)' });
const isRetina = useMediaQuery({ query: '(min-resolution: 2dppx)' });
const isMobile = useMediaQuery({ query: '(max-width: 768px)' });
// TODO: height checks ?

const getDeviceTypeInfo = () => {
Expand All @@ -40,7 +42,8 @@ function App() {
isBigScreen: isBigScreen,
isTabletOrMobile: isTabletOrMobile,
isPortrait: isPortrait,
isRetina: isRetina
isRetina: isRetina,
isMobile: isMobile
};
};

Expand Down Expand Up @@ -518,6 +521,7 @@ function App() {
activeTab={activeTab}
setActiveTab={setActiveTab}
getDeviceTypeInfo={getDeviceTypeInfo}
isMobile={isMobile}
templateOverlayMode={templateOverlayMode}
setTemplateOverlayMode={setTemplateOverlayMode}
overlayTemplate={overlayTemplate}
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/canvas/Canvas.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@
background-color: rgba(120, 120, 120, 1);
image-rendering: pixelated;
box-shadow: 0 0 0.05rem 0.01rem rgba(0, 0, 0, 0.4);
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently supported by Chrome, Edge, Opera and Firefox */
}
78 changes: 76 additions & 2 deletions frontend/src/canvas/CanvasContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ const CanvasContainer = (props) => {
const [canvasX, setCanvasX] = useState(0);
const [canvasY, setCanvasY] = useState(0);
const [canvasScale, setCanvasScale] = useState(3.85);

const [touchInitialDistance, setInitialTouchDistance] = useState(0);
const [touchScale, setTouchScale] = useState(0);
const canvasContainerRef = useRef(null);

const [isDragging, setIsDragging] = useState(false);
Expand Down Expand Up @@ -48,6 +49,7 @@ const CanvasContainer = (props) => {
};

const handlePointerMove = (e) => {
if (props.nftMintingMode && !props.nftSelected) return;
if (isDragging) {
setCanvasX(canvasX + e.clientX - dragStartX);
setCanvasY(canvasY + e.clientY - dragStartY);
Expand Down Expand Up @@ -104,12 +106,84 @@ const CanvasContainer = (props) => {
setCanvasY(newPosY);
};

const handleTouchStart = (e) => {
if (e.touches.length === 2) {
const touch1 = e.touches[0];
const touch2 = e.touches[1];
const initialDistance = Math.sqrt(
Math.pow(touch2.clientX - touch1.clientX, 2) +
Math.pow(touch2.clientY - touch1.clientY, 2)
);
setTouchScale(canvasScale);
setInitialTouchDistance(initialDistance);
}
};

const handleTouchMove = (e) => {
if (e.touches.length === 2) {
const [touch1, touch2] = e.touches;
const distance = Math.sqrt(
Math.pow(touch2.clientX - touch1.clientX, 2) +
Math.pow(touch2.clientY - touch1.clientY, 2)
);
const rect = props.canvasRef.current.getBoundingClientRect();
const midX = (touch1.clientX + touch2.clientX) / 2;
const midY = (touch1.clientY + touch2.clientY) / 2;

let cursorX = midX - rect.left;
let cursorY = midY - rect.top;
if (cursorX < 0) {
cursorX = 0;
} else if (cursorX > rect.width) {
cursorX = rect.width;
}
if (cursorY < 0) {
cursorY = 0;
} else if (cursorY > rect.height) {
cursorY = rect.height;
}

let newScale = (distance / touchInitialDistance) * touchScale;
if (newScale < minScale) {
newScale = minScale;
} else if (newScale > maxScale) {
newScale = maxScale;
}
const newWidth = width * newScale;
const newHeight = height * newScale;

const oldCursorXRelative = cursorX / rect.width;
const oldCursorYRelative = cursorY / rect.height;

const newCursorX = oldCursorXRelative * newWidth;
const newCursorY = oldCursorYRelative * newHeight;

const newPosX = canvasX - (newCursorX - cursorX);
const newPosY = canvasY - (newCursorY - cursorY);

setCanvasScale(newScale);
setCanvasX(newPosX);
setCanvasY(newPosY);
// TODO: Make scroll acceleration based
}
};

useEffect(() => {
canvasContainerRef.current.addEventListener('wheel', zoom);
canvasContainerRef.current.addEventListener('touchstart', handleTouchStart);
canvasContainerRef.current.addEventListener('touchmove', handleTouchMove);
return () => {
canvasContainerRef.current.removeEventListener('wheel', zoom);
canvasContainerRef.current.removeEventListener(
'touchstart',
handleTouchStart
);
canvasContainerRef.current.removeEventListener(
'touchmove',
handleTouchMove
);
};
}, [canvasScale, canvasX, canvasY]);
}, [canvasScale, canvasX, canvasY, touchInitialDistance]);

// Init canvas transform to center of the viewport
useEffect(() => {
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/tabs/ExpandableTab.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,11 @@ const ExpandableTab = (props) => {
{props.title}
</h1>
<div className='ExpandableTab__content'>
<MainSection {...rest} />
{(!props.expanded || (props.expanded && !props.isMobile)) && (
<MainSection {...rest} />
)}
{props.expanded &&
!props.isMobile &&
(props.canExpand === undefined || props.canExpand) && (
<div className='ExpandableTab__divider' />
)}
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/tabs/TabPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ const TabPanel = (props) => {
factionPixelsData={props.factionPixelsData}
setTemplateOverlayMode={props.setTemplateOverlayMode}
setOverlayTemplate={props.setOverlayTemplate}
isMobile={props.isMobile}
/>
</div>
)}
Expand Down Expand Up @@ -172,6 +173,7 @@ const TabPanel = (props) => {
latestMintedTokenId={props.latestMintedTokenId}
setLatestMintedTokenId={props.setLatestMintedTokenId}
queryAddress={props.queryAddress}
isMobile={props.isMobile}
/>
</div>
)}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/tabs/factions/Factions.js
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,7 @@ const Factions = (props) => {
clearFactionSelection={clearFactionSelection}
setTemplateOverlayMode={props.setTemplateOverlayMode}
setOverlayTemplate={props.setOverlayTemplate}
isMobile={props.isMobile}
/>
);
};
Expand Down
1 change: 0 additions & 1 deletion frontend/src/tabs/nfts/NFTMintingPanel.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
.NFTMintingPanel {
position: relative;
width: 100%;
height: 18vh;
padding: 1rem 0.5rem 0.5rem 0.5rem;
margin-bottom: 0.3rem;

Expand Down
1 change: 1 addition & 0 deletions frontend/src/tabs/nfts/NFTs.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ const NFTs = (props) => {
activeFilter={activeFilter}
setActiveFilter={setActiveFilter}
filters={filters}
isMobile={props.isMobile}
/>
);
};
Expand Down
58 changes: 50 additions & 8 deletions frontend/src/utils/Window.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,32 @@ export function usePreventZoom(scrollCheck = true, keyboardCheck = true) {
useEffect(() => {
const handleKeydown = (e) => {
if (
keyboardCheck &&
e.ctrlKey &&
(e.keyCode === '61' ||
e.keyCode === '107' ||
e.keyCode === '173' ||
e.keyCode === '109' ||
e.keyCode === '187' ||
e.keyCode === '189')
(keyboardCheck && e.ctrlKey) ||
(e.metaKey &&
(e.keyCode === '61' ||
e.keyCode === '107' ||
e.keyCode === '173' ||
e.keyCode === '109' ||
e.keyCode === '187' ||
e.key === '=' ||
e.key === '-' ||
e.key === '+' ||
e.keyCode === '189'))
) {
e.preventDefault();
}
};
const handleTouchStart = (event) => {
if (event.touches.length > 1) {
event.preventDefault();
}
};

const handleTouchMove = (event) => {
if (event.scale !== 1) {
event.preventDefault();
}
};

const handleWheel = (e) => {
if (scrollCheck && e.ctrlKey) {
Expand All @@ -25,10 +39,38 @@ export function usePreventZoom(scrollCheck = true, keyboardCheck = true) {

document.addEventListener('keydown', handleKeydown);
document.addEventListener('wheel', handleWheel, { passive: false });
document.addEventListener('touchstart', handleTouchStart, {
passive: false
});
document.addEventListener('touchmove', handleTouchMove, { passive: false });

return () => {
document.removeEventListener('keydown', handleKeydown);
document.removeEventListener('wheel', handleWheel);
document.removeEventListener('touchstart', handleTouchStart);
document.removeEventListener('touchmove', handleTouchMove);
};
}, [scrollCheck, keyboardCheck]);
}

export const useLockScroll = (scrollCheck = true) => {
useEffect(() => {
const handleScroll = (e) => {
if (scrollCheck) {
e.preventDefault();
}
};

if (scrollCheck) {
document.body.style.overflow = 'hidden';
document.addEventListener('scroll', handleScroll, { passive: false });
} else {
document.body.style.overflow = 'auto';
document.removeEventListener('scroll', handleScroll);
}

return () => {
document.removeEventListener('scroll', handleScroll);
};
}, [scrollCheck]);
};

0 comments on commit c8b4f8b

Please sign in to comment.