Svelte 4中获取inline-block元素的正确屏幕位置
Svelte 4组件中获取元素最终屏幕位置不准确的原因及解决方法
问题原因
你代码里给div加了in:scale过渡动画,这个动画会在组件挂载后持续500ms执行。onMount里的await tick()只能等待组件首次渲染完成,但此时过渡动画还在运行,元素的位置处于动态变化中,这时候调用getBoundingClientRect()拿到的是动画过程中的中间位置,自然和视觉上最终静止的位置不一致。
解决方法
方法1:监听过渡结束事件(推荐)
直接监听元素的transitionend事件,等动画完全结束后再计算位置,这是最可靠的方式:
<script> import { onMount } from "svelte" import { scale } from "svelte/transition" import { quintIn } from "svelte/easing" export let title export let htmlContent = null let positionCalculated = false let windowDiv onMount(() => { if (!windowDiv || positionCalculated) return; const handleTransitionEnd = () => { const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0; const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0; const box = windowDiv.getBoundingClientRect(); const posTop = box.top + scrollTop; const posLeft = box.left + scrollLeft; console.log(`Initial position of ${title}: left: ${posLeft}, top: ${posTop}`); positionCalculated = true; // 用完移除事件监听,避免内存泄漏 windowDiv.removeEventListener('transitionend', handleTransitionEnd); }; windowDiv.addEventListener('transitionend', handleTransitionEnd); }) </script> <div class="window" bind:this={windowDiv} in:scale={{ duration: 500, easing: quintIn }} > <h1 class="window-title">{title}</h1> {#if htmlContent} <div class="content">{@html htmlContent}</div> {/if} </div> <style> .window { display: inline-block; min-width: 250px; max-width: 600px; } </style>
方法2:延迟等待动画完成
如果确定动画时长固定,可以直接延迟对应时间后再计算位置,但这种方式不够灵活,若后续修改动画时长需要同步调整延迟时间:
<script> import { onMount, tick } from "svelte" import { scale } from "svelte/transition" import { quintIn } from "svelte/easing" export let title export let htmlContent = null let positionCalculated = false let windowDiv onMount(async () => { await tick(); // 等待动画的500ms时长 setTimeout(() => { if (windowDiv && !positionCalculated) { const scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0; const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0; const box = windowDiv.getBoundingClientRect(); const posTop = box.top + scrollTop; const posLeft = box.left + scrollLeft; console.log(`Initial position of ${title}: left: ${posLeft}, top: ${posTop}`); positionCalculated = true; } }, 500); }) </script> <!-- template和style部分不变 -->
补充说明
因为组件被多次实例化,每个实例的windowDiv都是独立的DOM元素,所以上述两种方法都能准确获取每个组件实例对应元素的最终位置,不会互相干扰。
内容的提问来源于stack exchange,提问作者bolino
相关产品推荐
相关产品推荐

