You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 01:37:13