style: 调整Select动画样式
- 动画采用Svelte的原生transition语法
This commit is contained in:
@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { Select as BitsSelect, type WithoutChildren } from 'bits-ui';
|
||||
import { elasticOut, cubicOut } from 'svelte/easing';
|
||||
|
||||
type Props = WithoutChildren<BitsSelect.ContentProps> & {
|
||||
items: { value: string; label: string; disabled?: boolean }[];
|
||||
@ -7,14 +8,61 @@
|
||||
};
|
||||
|
||||
let { items, class: className = '', ...restProps }: Props = $props();
|
||||
|
||||
function selectTransition(
|
||||
node: HTMLElement,
|
||||
params: {
|
||||
delay?: number;
|
||||
duration?: number;
|
||||
easing?: (t: number) => number;
|
||||
y?: number;
|
||||
start?: number;
|
||||
} = {}
|
||||
) {
|
||||
const {
|
||||
delay = 0,
|
||||
duration = 1000,
|
||||
easing = cubicOut,
|
||||
y = -6,
|
||||
start = 0.95,
|
||||
} = params;
|
||||
|
||||
const existingTransform = getComputedStyle(node).transform.replace(
|
||||
'none',
|
||||
''
|
||||
);
|
||||
|
||||
return {
|
||||
delay,
|
||||
duration,
|
||||
easing,
|
||||
css: (t: number, u: number) => {
|
||||
const translate = `translateY(${u * y}px)`;
|
||||
const scale = `scale(${start + t * (1 - start)})`;
|
||||
|
||||
return `
|
||||
opacity: ${t};
|
||||
transform: ${existingTransform} ${translate} ${scale};
|
||||
`;
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<BitsSelect.Portal>
|
||||
<BitsSelect.Content
|
||||
forceMount
|
||||
sideOffset={4}
|
||||
class={`data=[state=closed]:fade-out-0 select-motion w-(--bits-select-anchor-width) min-w-(--bits-select-anchor-width) rounded-xl border-[1.5px] border-base-content/20 bg-base-100 select-none ${className}`}
|
||||
class={`w-(--bits-select-anchor-width) min-w-(--bits-select-anchor-width) rounded-xl border-[1.5px] border-base-content/20 bg-base-100 select-none ${className}`}
|
||||
{...restProps}
|
||||
>
|
||||
{#snippet child({ wrapperProps, props, open })}
|
||||
{#if open}
|
||||
<div {...wrapperProps}>
|
||||
<div
|
||||
{...props}
|
||||
in:selectTransition={{ easing: elasticOut }}
|
||||
out:selectTransition={{ duration: 200 }}
|
||||
>
|
||||
<BitsSelect.ScrollUpButton>up</BitsSelect.ScrollUpButton>
|
||||
<BitsSelect.Viewport class="p-1">
|
||||
@ -30,25 +78,9 @@
|
||||
{/each}
|
||||
</BitsSelect.Viewport>
|
||||
<BitsSelect.ScrollDownButton>down</BitsSelect.ScrollDownButton>
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/snippet}
|
||||
</BitsSelect.Content>
|
||||
</BitsSelect.Portal>
|
||||
|
||||
<style lang="postcss">
|
||||
@reference "tailwindcss";
|
||||
|
||||
:global(.select-content-animate) {
|
||||
@apply transition-[opacity,transform] duration-200 ease-out will-change-transform;
|
||||
}
|
||||
|
||||
:global(.select-content-animate[data-state='open']) {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
:global(.select-content-animate[data-state='closed']) {
|
||||
opacity: 0;
|
||||
transform: translateY(-10px);
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
@layer components {
|
||||
.select-motion {
|
||||
@apply transition-all duration-200 ease-out will-change-transform;
|
||||
}
|
||||
|
||||
.select-motion[data-state="open"] {
|
||||
@apply pointer-events-auto translate-y-0 scale-100 opacity-100;
|
||||
}
|
||||
|
||||
.select-motion[data-state="closed"] {
|
||||
@apply pointer-events-none -translate-y-2 scale-95 opacity-0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user