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

Svelte列表crossfade动画随机错位问题排查与解决

列表项选中动画问题及优化过程

我想要实现这样的列表动画效果:选中列表项时,该项滑动到顶部,其余项飞出;再次选中时,该项回到原位置,其余项飞回。参考crossfade示例完成了一个实现,但测试发现元素有时无法回到初始位置,只有再次触发动画才可能修复。确定是crossfade函数需要优化,或者应该改用常规transition,但不知道具体怎么操作。

初始实现代码

<script>
  import { flip } from 'svelte/animate'
  import { writable } from 'svelte/store'
  import { crossfade, fly } from 'svelte/transition'

  const ACTIONS = [
    { key: 'blue' },
    { key: 'green' },
    { key: 'yellow' },
    { key: 'purple' },
    { key: 'orange' },
    { key: 'red' },
  ]
  const selectedAction = writable(null)
  let shownActions = ACTIONS.map(a => a)

  const [send, receive] = crossfade({
    fallback(node, _params) {
      return fly(node, { x: -800, duration: 600 })
    }
  })

  function selectAction(action) {
    if ($selectedAction === action) {
      shownActions = ACTIONS.map(a => a)
      selectedAction.update(v => {
        if (v === action || action === undefined) {
          return null
        } else {
          return action
        }
      })
    } else {
      shownActions = ACTIONS.filter(a => a.key === action)
      selectedAction.update(v => {
        if (v === action || action === undefined) {
          return null
        } else {
          return action
        }
      })
      window.scrollTo({
        top: 0,
        behavior: 'smooth'
      })
    }
  }
</script>

<ul>
{#each shownActions as action (action)}
  <li
    class={`${$$props.class || ''}`}
    animate:flip={{ duration: 600 }}
    in:receive={{ key: action.key }}
    out:send={{ key: action.key }}
  >
    <button on:click={e => selectAction(action.key)}>{action.key}</button>
  </li>
{/each}
</ul>

<style lang="scss">
    ul {
        list-style: none;
    }
    li {
        display: flex;
        margin-bottom: 1rem;
    }
    button {
        margin-right: 1rem;
        padding: 0.5rem 1rem;
    }
</style>

编辑说明

最终我移除了列表飞出动画,仅保留单个项的滑动效果,视觉上更简洁。升级Svelte版本后,动画问题有所缓解——此前存在动画元素未正确重置导致挤压的情况。

所有项的进出动画代码

{#each shownActions as action (action)}
  <li
    class={`${$$props.class || ''}`}
    animate:flip={{ duration: 500 }}
    in:fly={{ delay: 0, duration: 500 }}
  >
    <div
      class={`${$$props.class || ''}`}
      in:receive={{ key: action.key }}
      out:send={{ key: action.key }}
    >
      <button on:click={e => selectAction(action.key)}>{action.key}</button>
    </div>
  </li>
{/each}

偏好的无其余项飞出动画代码

{#each shownActions as action (action)}
  <li
    class={`${$$props.class || ''}`}
    animate:flip={{ duration: 500 }}
    in:receive={{ key: action.key }}
  >
    <button on:click={e => selectAction(action.key)}>{action.key}</button>
  </li>
{/each}

内容的提问来源于stack exchange,提问作者TeemuK

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 21:46:33