如何在Svelte中根据后端返回结果为指定输入框应用Boop CSS动画
实现方案
核心不需要引入额外组件,用Svelte原生的自定义action(action)就能实现,这是Svelte专门给DOM元素附加可复用交互/动画逻辑的原生能力,比封装组件更轻量适配这个场景。
具体步骤
- 先改造通用boop action,增加程序化触发的支持:原默认实现是hover触发,我们新增
trigger参数,当参数从false变为true时自动执行boop动画,执行完自动复位。 - 给每个输入框单独维护一个动画触发状态,初始全部为false。
- 提交表单拿到后端返回的无效位列表后,先重置所有触发状态,等DOM更新完成后,把无效位对应的触发状态设为true,就会自动触发对应输入框的动画。
完整代码
<script> import { spring } from 'svelte/motion'; import { tick } from 'svelte'; // 存储输入框DOM引用 let letterPlaceHolder = new Array(50); // 存储每个输入框的boop动画触发状态,初始全为未触发 let boopTriggers = new Array(50).fill(false); /** * 自定义boop action,支持程序化触发 * 动画参数可按需调整 */ function boop(node, params = {}) { const { x = 0, y = 0, rotation = 0, scale = 1, duration = 150, trigger = false } = params; // 用spring做弹性动画 const transformSpring = spring( { x: 0, y: 0, rotation: 0, scale: 1 }, { stiffness: 0.1, damping: 0.15 } ); // 订阅动画值变化,实时更新DOM样式 const unsubscribe = transformSpring.subscribe(($transform) => { node.style.transform = `translate3d(${$transform.x}px, ${$transform.y}px, 0) rotate(${$transform.rotation}deg) scale(${$transform.scale})`; }); // 执行boop动画 const runAnimation = async () => { // 先强制复位 transformSpring.set({ x: 0, y: 0, rotation: 0, scale: 1 }, { hard: true }); // 应用动画偏移 await transformSpring.set({ x, y, rotation, scale }, { hard: true }); // 到达动画峰值后停留指定时长,再复位 setTimeout(() => { transformSpring.set({ x: 0, y: 0, rotation: 0, scale: 1 }); }, duration); }; // 初始挂载时如果trigger为true就执行一次 if (trigger) runAnimation(); return { // 绑定的参数更新时自动调用 update(updatedParams) { // 只有trigger从false切到true的时候才触发动画,避免无效执行 if (updatedParams.trigger && !params.trigger) { runAnimation(); } params = updatedParams; }, // 元素销毁时取消订阅,避免内存泄漏 destroy() { unsubscribe(); } }; } async function submit() { // 第一步:重置所有触发状态 boopTriggers = boopTriggers.map(() => false); // 等状态同步到DOM,保证连续提交时同一个无效位的动画可以重复触发 await tick(); // 替换成你的实际接口请求逻辑 const response = await fetch('/api/validate-letters'); // 约定后端返回长度为50的布尔数组,索引对应输入框位置,值为true代表该位输入无效 const invalidFlags = await response.json(); // 标记所有无效位,触发对应动画 invalidFlags.forEach((isInvalid, index) => { if (isInvalid) boopTriggers[index] = true; }); // 手动触发数组的响应式更新 boopTriggers = boopTriggers; } </script> <style> input { margin: 2px; width: 30px; height: 30px; text-align: center; } </style> {#each Array(50) as _, i} <input type="text" maxlength="1" bind:this={letterPlaceHolder[i]} use:boop={{ x: 2, rotation: 8, duration: 120, trigger: boopTriggers[i] }} /> {/each} <button id="submit" on:click={submit}>Submit</button>
关键说明
- 每次提交前必须先重置所有触发状态再调用
tick(),否则同一个输入框连续两次校验无效时,因为trigger值没有变化,不会触发动画更新。 - boop的动画参数(x/y偏移、旋转角度、缩放比例、动画时长)可以直接在
use:boop的传参里调整,和通用boop效果完全一致。 - 如果需要在动画结束后加其他逻辑,直接在
runAnimation里的setTimeout回调中补充即可。
内容的提问来源于stack exchange,提问作者Hanus á Tjaldrafløtti
相关产品推荐
相关产品推荐

