diff --git a/src/lib/components/tiles/Tile.svelte b/src/lib/components/tiles/Tile.svelte index 6e28f91..e69fd83 100644 --- a/src/lib/components/tiles/Tile.svelte +++ b/src/lib/components/tiles/Tile.svelte @@ -9,6 +9,11 @@ Metro-like tile. Must be in a group to display correctly. import { cva } from "class-variance-authority"; import { onDestroy, onMount } from "svelte"; + /** + * Max angle value. + */ + const MAX_ANGLE = 10; + /** * Base size of a icon in pixels. **/ @@ -27,22 +32,22 @@ Metro-like tile. Must be in a group to display correctly. /** * Base size of a page in pixels. */ - const PAGE_BASE_SIZE = 75; + const TILE_BASE_SIZE = 75; /** * Page heights. */ const PAGE_HEIGHTS = { - small: PAGE_BASE_SIZE, - medium: PAGE_BASE_SIZE * 2, - wide: PAGE_BASE_SIZE * 2, - large: PAGE_BASE_SIZE * 4, + small: TILE_BASE_SIZE, + medium: TILE_BASE_SIZE * 2, + wide: TILE_BASE_SIZE * 2, + large: TILE_BASE_SIZE * 4, }; /** * Max scroll interval delay in milliseconds. */ - const SCROLL_INTERVAL_MAX = 10 * 1000; + const SCROLL_INTERVAL_MAX = 7.5 * 1000; /** * Min scroll interval delay in milliseconds. @@ -50,9 +55,9 @@ Metro-like tile. Must be in a group to display correctly. const SCROLL_INTERVAL_MIN = SCROLL_INTERVAL_MAX / 2; /** - * Tile element. + * Tile pages. */ - let element: HTMLElement; + let pages: HTMLElement; /** * Properties. @@ -94,7 +99,18 @@ Metro-like tile. Must be in a group to display correctly. * Tile style. **/ const style = cva( - ["bg-(--tile-color)", "overflow-y-hidden", "scroll-smooth"], + [ + "relative", + "bg-(--tile-color)", + "perspective-distant", + "active:scale-95", + "active:transform-gpu", + "active:transform-3d", + "active:rotate-x-(--tile-rotate-x)", + "active:rotate-y-(--tile-rotate-y)", + "duration-75", + "ease-in-out", + ], { variants: { size: { @@ -117,6 +133,38 @@ Metro-like tile. Must be in a group to display correctly. */ const pageHeight = PAGE_HEIGHTS[size]; + /** + * Mouse position. Used by border (on mouse over). + */ + let mousePosition = $state({ x: 0, y: 0 }); + + /** + * Rotation values. Used by transform (on click). + */ + let rotationValues = $state({ x: 0, y: 0 }); + + /** + * Updates the mouse position-based values. + * @param event `mouseover` event. + */ + function updateDynamicMouseValues(event: MouseEvent) { + const element = event.target as HTMLElement; + const bounds = element.getBoundingClientRect(); + + mousePosition = { + x: event.clientX - bounds.left, + y: event.clientY - bounds.top, + }; + + const tileCenterX = bounds.width / 2; + const tileCenterY = bounds.height / 2; + + rotationValues.x = + -((mousePosition.y - tileCenterY) / tileCenterY) * MAX_ANGLE; + rotationValues.y = + -((tileCenterX - mousePosition.x) / tileCenterX) * MAX_ANGLE; + } + /** * Current page number. */ @@ -147,7 +195,7 @@ Metro-like tile. Must be in a group to display correctly. } // NOTE: Scroll the pages. - element.scrollBy({ top: pageHeight * scrollMultipler }); + pages.scrollBy({ top: pageHeight * scrollMultipler }); // NOTE: Change the orientation after scroll. if (currentPage === pageCount) { @@ -163,7 +211,7 @@ Metro-like tile. Must be in a group to display correctly. let scrollInterval: NodeJS.Timeout; onMount(() => { - pageCount = Math.floor(element.scrollHeight / pageHeight); + pageCount = Math.floor(pages.scrollHeight / pageHeight); if (size === "small" || pageCount === 1) { return; @@ -186,17 +234,45 @@ Metro-like tile. Must be in a group to display correctly. }); -