From efde7780a025f90c8df08659673f6f8d626b1b16 Mon Sep 17 00:00:00 2001 From: Mark Mankarious Date: Mon, 30 Oct 2023 22:31:17 +0000 Subject: [PATCH] feat: applies animation on zoom and scroll --- src/components/Grid/Grid.tsx | 41 +++++++++++++++++------- src/components/SceneLayer/SceneLayer.tsx | 20 +++++++++--- 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/components/Grid/Grid.tsx b/src/components/Grid/Grid.tsx index f81687b..8d052f1 100644 --- a/src/components/Grid/Grid.tsx +++ b/src/components/Grid/Grid.tsx @@ -1,20 +1,42 @@ -import React, { useMemo } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { Box } from '@mui/material'; +import gsap from 'gsap'; +import { Size } from 'src/types'; import gridTileSvg from 'src/assets/grid-tile-bg.svg'; import { useUiStateStore } from 'src/stores/uiStateStore'; import { PROJECTED_TILE_SIZE } from 'src/config'; import { SizeUtils } from 'src/utils/SizeUtils'; export const Grid = () => { + const elementRef = useRef(null); + const [isFirstRender, setIsFirstRender] = useState(true); const scroll = useUiStateStore((state) => { return state.scroll; }); const zoom = useUiStateStore((state) => { return state.zoom; }); - const projectedTileSize = useMemo(() => { - return SizeUtils.multiply(PROJECTED_TILE_SIZE, zoom); - }, [zoom]); + + useEffect(() => { + 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 ( { }} > diff --git a/src/components/SceneLayer/SceneLayer.tsx b/src/components/SceneLayer/SceneLayer.tsx index 1c05f9d..86f3a09 100644 --- a/src/components/SceneLayer/SceneLayer.tsx +++ b/src/components/SceneLayer/SceneLayer.tsx @@ -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 { useUiStateStore } from 'src/stores/uiStateStore'; @@ -9,6 +10,8 @@ interface Props { } export const SceneLayer = ({ children, order = 0, sx }: Props) => { + const elementRef = useRef(null); + const scroll = useUiStateStore((state) => { return state.scroll; }); @@ -16,8 +19,20 @@ export const SceneLayer = ({ children, order = 0, sx }: Props) => { 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 ( { userSelect: 'none', ...sx }} - style={{ - transform: `translate(${scroll.position.x}px, ${scroll.position.y}px) scale(${zoom})` - }} > {children}