feat(PerspectivePlan): add a wrapper to work with perspective manager styles
This commit is contained in:
83
src/shared/ui/PerspectivePlan/PerspectivePlan.svelte
Normal file
83
src/shared/ui/PerspectivePlan/PerspectivePlan.svelte
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<!--
|
||||||
|
Component: PerspectivePlan
|
||||||
|
Wrapper that applies perspective transformations based on back/front state.
|
||||||
|
Style computation moved from manager to component for simpler architecture.
|
||||||
|
-->
|
||||||
|
<script lang="ts">
|
||||||
|
import type { PerspectiveManager } from '$shared/lib';
|
||||||
|
import { cn } from '$shared/shadcn/utils/shadcn-utils';
|
||||||
|
import { type Snippet } from 'svelte';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
/**
|
||||||
|
* Perspective manager
|
||||||
|
*/
|
||||||
|
manager: PerspectiveManager;
|
||||||
|
/**
|
||||||
|
* Additional classes
|
||||||
|
*/
|
||||||
|
class?: string;
|
||||||
|
/**
|
||||||
|
* Children
|
||||||
|
*/
|
||||||
|
children: Snippet<[{ className?: string }]>;
|
||||||
|
/**
|
||||||
|
* Constrain plan to a horizontal region
|
||||||
|
* 'left' | 'right' | 'full' (default)
|
||||||
|
*/
|
||||||
|
region?: 'left' | 'right' | 'full';
|
||||||
|
/**
|
||||||
|
* Width percentage when using left/right region (default 50)
|
||||||
|
*/
|
||||||
|
regionWidth?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
let { manager, children, class: className = '', region = 'full', regionWidth = 50 }: Props = $props();
|
||||||
|
|
||||||
|
const config = $derived(manager.getConfig());
|
||||||
|
|
||||||
|
// Computed style based on spring position (0 = front, 1 = back)
|
||||||
|
const style = $derived.by(() => {
|
||||||
|
const distance = manager.spring.current;
|
||||||
|
const baseX = config.horizontalOffset ?? 0;
|
||||||
|
|
||||||
|
// Back state: blurred, scaled down, pushed back
|
||||||
|
// Front state: fully visible, in focus
|
||||||
|
const scale = 1 - distance * (config.scaleStep ?? 0.5);
|
||||||
|
const blur = distance * (config.blurStep ?? 4);
|
||||||
|
const opacity = Math.max(0, 1 - distance * (config.opacityStep ?? 0.5));
|
||||||
|
const zIndex = 10;
|
||||||
|
const pointerEvents = distance < 0.4 ? 'auto' : ('none' as const);
|
||||||
|
|
||||||
|
return {
|
||||||
|
transform: `translate3d(${baseX}px, 0px, ${-distance * (config.depthStep ?? 100)}px) scale(${scale})`,
|
||||||
|
filter: `blur(${blur}px)`,
|
||||||
|
opacity,
|
||||||
|
pointerEvents,
|
||||||
|
zIndex,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Calculate horizontal constraints based on region
|
||||||
|
const regionStyleStr = $derived(() => {
|
||||||
|
if (region === 'full') return '';
|
||||||
|
const side = region === 'left' ? 'left' : 'right';
|
||||||
|
return `position: absolute; ${side}: 0; width: ${regionWidth}%; top: 0; bottom: 0;`;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Visibility: front = visible, back = hidden
|
||||||
|
const isVisible = $derived(manager.isFront);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class={cn('will-change-transform', className)}
|
||||||
|
style:transform-style="preserve-3d"
|
||||||
|
style:transform={style?.transform}
|
||||||
|
style:filter={style?.filter}
|
||||||
|
style:opacity={style?.opacity}
|
||||||
|
style:pointer-events={style?.pointerEvents}
|
||||||
|
style:z-index={style?.zIndex}
|
||||||
|
style:custom={regionStyleStr()}
|
||||||
|
>
|
||||||
|
{@render children({ className: isVisible ? 'visible' : 'hidden' })}
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user