feat: applies animation on zoom and scroll

This commit is contained in:
Mark Mankarious 2023-10-30 22:31:17 +00:00
parent 2230637a52
commit efde7780a0
2 changed files with 45 additions and 16 deletions

View file

@ -1,20 +1,42 @@
import React, { useMemo } from 'react'; import React, { useEffect, useRef, useState } from 'react';
import { Box } from '@mui/material'; import { Box } from '@mui/material';
import gsap from 'gsap';
import { Size } from 'src/types';
import gridTileSvg from 'src/assets/grid-tile-bg.svg'; import gridTileSvg from 'src/assets/grid-tile-bg.svg';
import { useUiStateStore } from 'src/stores/uiStateStore'; import { useUiStateStore } from 'src/stores/uiStateStore';
import { PROJECTED_TILE_SIZE } from 'src/config'; import { PROJECTED_TILE_SIZE } from 'src/config';
import { SizeUtils } from 'src/utils/SizeUtils'; import { SizeUtils } from 'src/utils/SizeUtils';
export const Grid = () => { export const Grid = () => {
const elementRef = useRef<HTMLDivElement>(null);
const [isFirstRender, setIsFirstRender] = useState(true);
const scroll = useUiStateStore((state) => { const scroll = useUiStateStore((state) => {
return state.scroll; return state.scroll;
}); });
const zoom = useUiStateStore((state) => { const zoom = useUiStateStore((state) => {
return state.zoom; return state.zoom;
}); });
const projectedTileSize = useMemo(() => {
return SizeUtils.multiply(PROJECTED_TILE_SIZE, zoom); useEffect(() => {
}, [zoom]); if (!elementRef.current) return;
const projectedTileSize = SizeUtils.multiply(PROJECTED_TILE_SIZE, zoom);
const elSize = elementRef.current.getBoundingClientRect();
const backgroundPosition: Size = {
width: elSize.width / 2 + scroll.position.x + projectedTileSize.width / 2,
height: elSize.height / 2 + scroll.position.y
};
gsap.to(elementRef.current, {
duration: isFirstRender ? 0 : 0.25,
backgroundSize: `${projectedTileSize.width}px`,
backgroundPosition: `${backgroundPosition.width}px ${backgroundPosition.height}px`
});
if (isFirstRender) {
setIsFirstRender(false);
}
}, [scroll, zoom, isFirstRender]);
return ( return (
<Box <Box
@ -29,17 +51,12 @@ export const Grid = () => {
}} }}
> >
<Box <Box
ref={elementRef}
sx={{ sx={{
position: 'absolute', position: 'absolute',
width: '100%', width: '100%',
height: '100%' height: '100%',
}} background: `repeat url("${gridTileSvg}")`
style={{
background: `repeat url("${gridTileSvg}")`,
backgroundSize: `${projectedTileSize.width}px`,
backgroundPosition: `calc(50% + ${
scroll.position.x % projectedTileSize.width
}px) calc(50% + ${scroll.position.y % projectedTileSize.height}px)`
}} }}
/> />
</Box> </Box>

View file

@ -1,4 +1,5 @@
import React from 'react'; import React, { useRef, useEffect } from 'react';
import gsap from 'gsap';
import { Box, SxProps } from '@mui/material'; import { Box, SxProps } from '@mui/material';
import { useUiStateStore } from 'src/stores/uiStateStore'; import { useUiStateStore } from 'src/stores/uiStateStore';
@ -9,6 +10,8 @@ interface Props {
} }
export const SceneLayer = ({ children, order = 0, sx }: Props) => { export const SceneLayer = ({ children, order = 0, sx }: Props) => {
const elementRef = useRef<HTMLDivElement>(null);
const scroll = useUiStateStore((state) => { const scroll = useUiStateStore((state) => {
return state.scroll; return state.scroll;
}); });
@ -16,8 +19,20 @@ export const SceneLayer = ({ children, order = 0, sx }: Props) => {
return state.zoom; return state.zoom;
}); });
useEffect(() => {
if (!elementRef.current) return;
gsap.to(elementRef.current, {
duration: 0.25,
translateX: scroll.position.x,
translateY: scroll.position.y,
scale: zoom
});
}, [zoom, scroll]);
return ( return (
<Box <Box
ref={elementRef}
sx={{ sx={{
position: 'absolute', position: 'absolute',
zIndex: order, zIndex: order,
@ -28,9 +43,6 @@ export const SceneLayer = ({ children, order = 0, sx }: Props) => {
userSelect: 'none', userSelect: 'none',
...sx ...sx
}} }}
style={{
transform: `translate(${scroll.position.x}px, ${scroll.position.y}px) scale(${zoom})`
}}
> >
{children} {children}
</Box> </Box>