mirror of
https://github.com/markmanx/isoflow.git
synced 2025-02-08 04:18:29 +00:00
feat: applies animation on zoom and scroll
This commit is contained in:
parent
2230637a52
commit
efde7780a0
2 changed files with 45 additions and 16 deletions
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue