Tooltip with Following Cursor
Tooltip의 positioning passthrough를 사용해 pointer 좌표를 virtual anchor처럼 따라가도록 배치합니다.
현재 pointer 위치를 기준으로 배치해요.
<script lang="ts">
import * as Tooltip from '@odbd/svelte/tooltip'
let pointer = $state({ x: 0, y: 0 })
// 추적 영역 안에 포인터가 있으면 무조건 열림 — open을 이 불리언으로만
// 제어하고 zag의 hover-close는 onOpenChange에서 무시한다(추적 중 플리커 방지).
let inside = $state(false)
let rafId: number | null = null
// 제어형 open: 추적 영역의 enter/leave로만 열고 닫는다. zag의 hover 모델에
// 맡기면 pointermove마다 close 후보가 생겨 추적이 끊긴다.
const positioning = {
placement: 'right-start' as const,
strategy: 'fixed' as const,
gutter: 14,
getAnchorRect: () => ({ x: pointer.x, y: pointer.y, width: 1, height: 1 }),
}
</script>
<Tooltip.Root open={inside} onOpenChange={() => {}} openDelay={0} closeDelay={0} {positioning}>
<Tooltip.Context>
{#snippet render(api)}
<Tooltip.Trigger
class="examples-cursor-target"
onpointerenter={(event: PointerEvent) => {
pointer = { x: event.clientX, y: event.clientY }
inside = true
}}
onpointerleave={() => {
inside = false
}}
onpointermove={(event: PointerEvent) => {
pointer = { x: event.clientX, y: event.clientY }
// 영역 위에서 움직이는 동안은 항상 열림. 가상 anchor가 pointer를
// 읽으므로 프레임당 1회 reposition하면 커서를 따라간다.
inside = true
if (rafId === null) {
rafId = requestAnimationFrame(() => {
rafId = null
api().reposition()
})
}
}}
>
이 영역에서 포인터를 움직여 보세요
</Tooltip.Trigger>
{/snippet}
</Tooltip.Context>
<Tooltip.Positioner>
<Tooltip.Content>현재 pointer 위치를 기준으로 배치해요.</Tooltip.Content>
</Tooltip.Positioner>
</Tooltip.Root>