feat(ComparisonSlider): integrate loader and add animations for appearance/disappearance
This commit is contained in:
@@ -15,8 +15,10 @@ import {
|
|||||||
createTypographyControl,
|
createTypographyControl,
|
||||||
} from '$shared/lib';
|
} from '$shared/lib';
|
||||||
import type { LineData } from '$shared/lib';
|
import type { LineData } from '$shared/lib';
|
||||||
|
import { Loader } from '$shared/ui';
|
||||||
import { comparisonStore } from '$widgets/ComparisonSlider/model';
|
import { comparisonStore } from '$widgets/ComparisonSlider/model';
|
||||||
import { Spring } from 'svelte/motion';
|
import { Spring } from 'svelte/motion';
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
import CharacterSlot from './components/CharacterSlot.svelte';
|
import CharacterSlot from './components/CharacterSlot.svelte';
|
||||||
import ControlsWrapper from './components/ControlsWrapper.svelte';
|
import ControlsWrapper from './components/ControlsWrapper.svelte';
|
||||||
import Labels from './components/Labels.svelte';
|
import Labels from './components/Labels.svelte';
|
||||||
@@ -26,6 +28,8 @@ import SliderLine from './components/SliderLine.svelte';
|
|||||||
const fontA = $derived(comparisonStore.fontA);
|
const fontA = $derived(comparisonStore.fontA);
|
||||||
const fontB = $derived(comparisonStore.fontB);
|
const fontB = $derived(comparisonStore.fontB);
|
||||||
|
|
||||||
|
const isLoading = $derived(comparisonStore.isLoading || !comparisonStore.isReady);
|
||||||
|
|
||||||
let container: HTMLElement | undefined = $state();
|
let container: HTMLElement | undefined = $state();
|
||||||
let controlsWrapperElement = $state<HTMLDivElement | null>(null);
|
let controlsWrapperElement = $state<HTMLDivElement | null>(null);
|
||||||
let measureCanvas: HTMLCanvasElement | undefined = $state();
|
let measureCanvas: HTMLCanvasElement | undefined = $state();
|
||||||
@@ -164,31 +168,33 @@ $effect(() => {
|
|||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
{#if fontA && fontB}
|
<!-- Hidden canvas used for text measurement by the helper -->
|
||||||
<!-- Hidden canvas used for text measurement by the helper -->
|
<canvas bind:this={measureCanvas} class="hidden" width="1" height="1"></canvas>
|
||||||
<canvas bind:this={measureCanvas} class="hidden" width="1" height="1"></canvas>
|
|
||||||
|
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div
|
<div
|
||||||
bind:this={container}
|
bind:this={container}
|
||||||
role="slider"
|
role="slider"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
aria-valuenow={Math.round(sliderPos)}
|
aria-valuenow={Math.round(sliderPos)}
|
||||||
aria-label="Font comparison slider"
|
aria-label="Font comparison slider"
|
||||||
onpointerdown={startDragging}
|
onpointerdown={startDragging}
|
||||||
class="
|
class="
|
||||||
group relative w-full py-16 px-24 sm:py-24 sm:px-24 overflow-hidden
|
group relative w-full py-16 px-24 sm:py-24 sm:px-24 overflow-hidden
|
||||||
rounded-[2.5rem]
|
rounded-[2.5rem]
|
||||||
select-none touch-none cursor-ew-resize min-h-100 flex flex-col justify-center
|
select-none touch-none cursor-ew-resize min-h-100 flex flex-col justify-center
|
||||||
backdrop-blur-lg bg-gradient-to-br from-gray-100/70 via-white/50 to-gray-100/60
|
backdrop-blur-lg bg-gradient-to-br from-gray-100/70 via-white/50 to-gray-100/60
|
||||||
border border-gray-300/40
|
border border-gray-300/40
|
||||||
shadow-[inset_0_4px_12px_0_rgba(0,0,0,0.12),inset_0_2px_4px_0_rgba(0,0,0,0.08),0_1px_2px_0_rgba(255,255,255,0.8)]
|
shadow-[inset_0_4px_12px_0_rgba(0,0,0,0.12),inset_0_2px_4px_0_rgba(0,0,0,0.08),0_1px_2px_0_rgba(255,255,255,0.8)]
|
||||||
before:absolute before:inset-0 before:rounded-[2.5rem] before:p-[1px]
|
before:absolute before:inset-0 before:rounded-[2.5rem] before:p-[1px]
|
||||||
before:bg-gradient-to-br before:from-black/5 before:via-black/2 before:to-transparent
|
before:bg-gradient-to-br before:from-black/5 before:via-black/2 before:to-transparent
|
||||||
before:-z-10 before:blur-sm
|
before:-z-10 before:blur-sm
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<!-- Text Rendering Container -->
|
<!-- Text Rendering Container -->
|
||||||
|
{#if isLoading}
|
||||||
|
<Loader size={24} />
|
||||||
|
{:else}
|
||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
relative flex flex-col items-center gap-4
|
relative flex flex-col items-center gap-4
|
||||||
@@ -197,6 +203,8 @@ $effect(() => {
|
|||||||
drop-shadow-[0_3px_6px_rgba(255,255,255,0.9)]
|
drop-shadow-[0_3px_6px_rgba(255,255,255,0.9)]
|
||||||
"
|
"
|
||||||
style:perspective="1000px"
|
style:perspective="1000px"
|
||||||
|
in:fade={{ duration: 300, delay: 300 }}
|
||||||
|
out:fade={{ duration: 300 }}
|
||||||
>
|
>
|
||||||
{#each charComparison.lines as line, lineIndex}
|
{#each charComparison.lines as line, lineIndex}
|
||||||
<div
|
<div
|
||||||
@@ -212,8 +220,10 @@ $effect(() => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SliderLine {sliderPos} {isDragging} />
|
<SliderLine {sliderPos} {isDragging} />
|
||||||
</div>
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if fontA && fontB && !isLoading}
|
||||||
<Labels {fontA} {fontB} {sliderPos} weight={weightControl.value} />
|
<Labels {fontA} {fontB} {sliderPos} weight={weightControl.value} />
|
||||||
<!-- Since there're slider controls inside we put them outside the main one -->
|
<!-- Since there're slider controls inside we put them outside the main one -->
|
||||||
<ControlsWrapper
|
<ControlsWrapper
|
||||||
@@ -226,5 +236,5 @@ $effect(() => {
|
|||||||
{sizeControl}
|
{sizeControl}
|
||||||
{heightControl}
|
{heightControl}
|
||||||
/>
|
/>
|
||||||
</div>
|
{/if}
|
||||||
{/if}
|
</div>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { ComboControlV2 } from '$shared/ui';
|
|||||||
import { ExpandableWrapper } from '$shared/ui';
|
import { ExpandableWrapper } from '$shared/ui';
|
||||||
import AArrowUP from '@lucide/svelte/icons/a-arrow-up';
|
import AArrowUP from '@lucide/svelte/icons/a-arrow-up';
|
||||||
import { Spring } from 'svelte/motion';
|
import { Spring } from 'svelte/motion';
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
/**
|
/**
|
||||||
@@ -121,6 +122,8 @@ $effect(() => {
|
|||||||
translateX({xSpring.current}px)
|
translateX({xSpring.current}px)
|
||||||
rotateZ({rotateSpring.current}deg)
|
rotateZ({rotateSpring.current}deg)
|
||||||
"
|
"
|
||||||
|
in:fade={{ duration: 300, delay: 300 }}
|
||||||
|
out:fade={{ duration: 300, delay: 300 }}
|
||||||
>
|
>
|
||||||
<ExpandableWrapper
|
<ExpandableWrapper
|
||||||
bind:element={wrapper}
|
bind:element={wrapper}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
} from '$shared/shadcn/ui/select';
|
} from '$shared/shadcn/ui/select';
|
||||||
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
||||||
import { comparisonStore } from '$widgets/ComparisonSlider/model';
|
import { comparisonStore } from '$widgets/ComparisonSlider/model';
|
||||||
|
import { fade } from 'svelte/transition';
|
||||||
|
|
||||||
interface Props<T> {
|
interface Props<T> {
|
||||||
/**
|
/**
|
||||||
@@ -60,6 +61,8 @@ function selectFontB(font: UnifiedFont) {
|
|||||||
<div
|
<div
|
||||||
class="z-50 pointer-events-auto"
|
class="z-50 pointer-events-auto"
|
||||||
onpointerdown={(e => e.stopPropagation())}
|
onpointerdown={(e => e.stopPropagation())}
|
||||||
|
in:fade={{ duration: 300, delay: 300 }}
|
||||||
|
out:fade={{ duration: 300, delay: 300 }}
|
||||||
>
|
>
|
||||||
<SelectRoot type="single" disabled={!fontList.length}>
|
<SelectRoot type="single" disabled={!fontList.length}>
|
||||||
<SelectTrigger
|
<SelectTrigger
|
||||||
|
|||||||
Reference in New Issue
Block a user