Svelte如何通过点击函数触发过渡动画而非内联方式?
在Svelte中实现点击触发的按钮动画(无冗余代码)
方案一:幽灵副本动画(按钮始终原位可见)
该方案通过创建按钮图标的"幽灵副本",实现点击时副本向上飞并淡出的效果,原始按钮保持可见且不位移。
步骤1:抽离SVG图标组件(避免重复代码)
创建Icon.svelte组件统一管理加减图标:
<script> export let name = 'minus'; // 支持传入"minus"或"plus"切换图标 </script> <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" viewBox="0 0 24 24" stroke="currentColor" role="presentation"> {#if name === 'minus'} <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 12H4" /> {:else} <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" /> {/if} </svg>
步骤2:主组件实现幽灵动画
<script> import { fade, fly } from 'svelte/transition'; import Icon from './Icon.svelte'; export let onAdd; export let onSubtract; // 控制幽灵显示状态 let showGhostMinus = false; let showGhostPlus = false; // 绑定按钮DOM元素用于计算位置 let minusBtn; let plusBtn; // 幽灵副本的定位样式 let ghostMinusStyle = {}; let ghostPlusStyle = {}; const handleMinusClick = () => { // 计算按钮相对于父容器的位置 const btnRect = minusBtn.getBoundingClientRect(); const parentRect = minusBtn.parentElement.getBoundingClientRect(); ghostMinusStyle = { left: `${btnRect.left - parentRect.left}px`, top: `${btnRect.top - parentRect.top}px`, position: 'absolute', pointerEvents: 'none' // 幽灵不干扰按钮交互 }; showGhostMinus = true; onSubtract?.(); // 动画结束后重置状态,确保下次点击可再次触发 setTimeout(() => showGhostMinus = false, 800); }; const handlePlusClick = () => { const btnRect = plusBtn.getBoundingClientRect(); const parentRect = plusBtn.parentElement.getBoundingClientRect(); ghostPlusStyle = { left: `${btnRect.left - parentRect.left}px`, top: `${btnRect.top - parentRect.top}px`, position: 'absolute', pointerEvents: 'none' }; showGhostPlus = true; onAdd?.(); setTimeout(() => showGhostPlus = false, 800); }; </script> <div class="plus-minus-btns" style="position: relative; display: flex; gap: 1rem; padding: 1rem;"> <!-- 原始减号按钮 --> <button class="btn btn-circle" bind:this={minusBtn} on:click={handleMinusClick}> <Icon name="minus" /> </button> <!-- 减号幽灵副本 --> {#if showGhostMinus} <div style={ghostMinusStyle} in:fly={{ y: -200, duration: 800 }} out:fade={{ duration: 600 }}> <Icon name="minus" /> </div> {/if} <!-- 原始加号按钮 --> <button class="btn btn-circle" bind:this={plusBtn} on:click={handlePlusClick}> <Icon name="plus" /> </button> <!-- 加号幽灵副本 --> {#if showGhostPlus} <div style={ghostPlusStyle} in:fly={{ y: -200, duration: 800 }} out:fade={{ duration: 600 }}> <Icon name="plus" /> </div> {/if} </div> <style> .btn-circle { width: 2.5rem; height: 2.5rem; padding: 0; border-radius: 50%; border: none; background: #e2e8f0; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: background 0.2s; } .btn-circle:hover { background: #cbd5e1; } </style>
方案二:按钮本身动画(动画后返回原位)
如果希望直接让按钮/图标执行动画后返回原位,可使用以下简化方案:
<script> import { fly } from 'svelte/transition'; import Icon from './Icon.svelte'; export let onAdd; export let onSubtract; // 控制动画触发状态 let animateMinus = false; let animatePlus = false; const handleMinusClick = () => { animateMinus = true; onSubtract?.(); // 动画结束后重置状态,回到初始位置 setTimeout(() => animateMinus = false, 800); }; const handlePlusClick = () => { animatePlus = true; onAdd?.(); setTimeout(() => animatePlus = false, 800); }; </script> <div class="plus-minus-btns" style="display: flex; gap: 1rem; padding: 1rem;"> <button class="btn btn-circle" on:click={handleMinusClick}> <div in:fly={{ y: animateMinus ? -200 : 0, duration: 800 }}> <Icon name="minus" /> </div> </button> <button class="btn btn-circle" on:click={handlePlusClick}> <div in:fly={{ y: animatePlus ? -200 : 0, duration: 800 }}> <Icon name="plus" /> </div> </button> </div> <style> .btn-circle { width: 2.5rem; height: 2.5rem; padding: 0; border-radius: 50%; border: none; background: #e2e8f0; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: background 0.2s; } .btn-circle:hover { background: #cbd5e1; } </style>
核心特性说明
- 仅使用
fade和fly过渡,无需cubicOut或tweened - 通过组件复用避免SVG代码重复
- 按钮始终保持可见且可交互
- 点击事件直接触发动画,逻辑简洁
内容的提问来源于stack exchange,提问作者Rin and Len
相关产品推荐
相关产品推荐

