feat(components/tiles): autoscroll between pages
This commit is contained in:
parent
fa6e1613f2
commit
ee42b07e6a
5 changed files with 122 additions and 26 deletions
|
|
@ -5,13 +5,55 @@ Metro-like tile. Must be in a group to display correctly.
|
|||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import randomNumber from "$lib/utils/random-number";
|
||||
import { cva } from "class-variance-authority";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
|
||||
/**
|
||||
* Base size for a icon in pixels.
|
||||
* Base size of a icon in pixels.
|
||||
**/
|
||||
const ICON_BASE_SIZE = 45;
|
||||
|
||||
/**
|
||||
* Icon sizes.
|
||||
*/
|
||||
const ICON_SIZES = {
|
||||
small: ICON_BASE_SIZE,
|
||||
medium: ICON_BASE_SIZE * 2,
|
||||
wide: ICON_BASE_SIZE * 2,
|
||||
large: ICON_BASE_SIZE * 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* Base size of a page in pixels.
|
||||
*/
|
||||
const PAGE_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,
|
||||
};
|
||||
|
||||
/**
|
||||
* Max scroll interval delay in milliseconds.
|
||||
*/
|
||||
const SCROLL_INTERVAL_MAX = 10 * 1000;
|
||||
|
||||
/**
|
||||
* Min scroll interval delay in milliseconds.
|
||||
*/
|
||||
const SCROLL_INTERVAL_MIN = SCROLL_INTERVAL_MAX / 2;
|
||||
|
||||
/**
|
||||
* Tile element.
|
||||
*/
|
||||
let element: HTMLElement;
|
||||
|
||||
/**
|
||||
* Properties.
|
||||
*/
|
||||
|
|
@ -45,43 +87,88 @@ Metro-like tile. Must be in a group to display correctly.
|
|||
/**
|
||||
* Tile style.
|
||||
**/
|
||||
const style = cva(["bg-(--tile-color)", "overflow-y-hidden"], {
|
||||
variants: {
|
||||
size: {
|
||||
small: ["row-span-1", "col-span-1"],
|
||||
medium: ["row-span-2", "col-span-2"],
|
||||
wide: ["row-span-2", "col-span-4"],
|
||||
large: ["row-span-4", "col-span-4"],
|
||||
const style = cva(
|
||||
["bg-(--tile-color)", "overflow-y-hidden", "scroll-smooth"],
|
||||
{
|
||||
variants: {
|
||||
size: {
|
||||
small: ["row-span-1", "col-span-1"],
|
||||
medium: ["row-span-2", "col-span-2"],
|
||||
wide: ["row-span-2", "col-span-4"],
|
||||
large: ["row-span-4", "col-span-4"],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
/**
|
||||
* Icon size. Based on tile's size.
|
||||
* Icon size.
|
||||
*/
|
||||
let iconSize: string = $state("0");
|
||||
switch (size) {
|
||||
case "small":
|
||||
iconSize = `${ICON_BASE_SIZE}px`;
|
||||
break;
|
||||
case "medium":
|
||||
case "wide":
|
||||
iconSize = `${ICON_BASE_SIZE * 2}px`;
|
||||
break;
|
||||
case "large":
|
||||
iconSize = `${ICON_BASE_SIZE * 4}px`;
|
||||
break;
|
||||
const iconSize = ICON_SIZES[size];
|
||||
|
||||
/**
|
||||
* Page height.
|
||||
*/
|
||||
const pageHeight = PAGE_HEIGHTS[size];
|
||||
|
||||
/**
|
||||
* Scroll side. Positive value is down, negative value is up.
|
||||
*/
|
||||
let scrollSide = 1;
|
||||
|
||||
/**
|
||||
* Get page count based on scroll height.
|
||||
*/
|
||||
const getPageCount = () => Math.floor(element.scrollHeight / pageHeight);
|
||||
|
||||
/**
|
||||
* Scroll between pages. Used by scroll interval.
|
||||
*/
|
||||
function scrollPages() {
|
||||
if (element.clientHeight === element.scrollHeight - pageHeight) {
|
||||
scrollSide = -scrollSide;
|
||||
}
|
||||
|
||||
element.scrollBy({ top: pageHeight * scrollSide });
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll interval.
|
||||
*/
|
||||
let scrollInterval: NodeJS.Timeout;
|
||||
|
||||
onMount(() => {
|
||||
if (size === "small" || getPageCount() === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scrollDelay = randomNumber(
|
||||
SCROLL_INTERVAL_MIN,
|
||||
SCROLL_INTERVAL_MAX,
|
||||
);
|
||||
|
||||
scrollInterval = setInterval(scrollPages, scrollDelay);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (!scrollInterval) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearInterval(scrollInterval);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div
|
||||
class={style({ size })}
|
||||
style="
|
||||
--tile-icon-size: {iconSize};
|
||||
--tile-icon-size: {iconSize}px;
|
||||
--tile-page-height: {pageHeight}px;
|
||||
--tile-name-display: {size !== 'small' ? 'inline' : 'none'};
|
||||
grid-row-start: {row};
|
||||
grid-column-start: {column};
|
||||
"
|
||||
bind:this={element}
|
||||
>
|
||||
{@render children()}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -22,8 +22,9 @@ Icon page for a tile. Must be in a tile to display correctly.
|
|||
</script>
|
||||
|
||||
<div
|
||||
class="h-full flex flex-col justify-end bg-center bg-no-repeat"
|
||||
class="flex flex-col justify-end bg-center bg-no-repeat"
|
||||
style="
|
||||
height: var(--tile-page-height);
|
||||
background-image: url('{icon}');
|
||||
background-size: var(--tile-icon-size);
|
||||
"
|
||||
|
|
|
|||
|
|
@ -16,6 +16,6 @@ Image page for a tile. Must be in a tile to display correctly.
|
|||
</script>
|
||||
|
||||
<div
|
||||
class="w-full h-full bg-cover bg-center bg-no-repeat"
|
||||
style="background-image: url('{image}');"
|
||||
class="w-full bg-cover bg-center bg-no-repeat"
|
||||
style="height: var(--tile-page-height); background-image: url('{image}');"
|
||||
></div>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue