如何在Vue3+Tailwind3中为选项切换添加挤压式CSS过渡动画?
解决液态滑动切换按钮的动画异常问题
问题背景
- 基于Vue3 + Tailwind3开发月度/年度切换按钮,需求是点击选项时,白色背景向选中方向过渡,呈现液态挤压的流畅交互效果
- 当前存在的问题:从Monthly切换到Yearly时动画正常,但从Yearly切回Monthly时,白色背景先向右侧偏移且无挤压效果(由
scaleX:1.5导致的侧边偏移破坏效果);尝试设置transform-origin: right会引发更严重的显示异常,现有CSS方案仅能勉强运行但存在侧边截断问题
修复方案
1. 统一动画逻辑,双向复用关键帧
替换原有的两个独立动画,改为单个动画配合动态transform-origin,通过切换类来控制左右方向的动画,确保双向切换都有一致的液态挤压/舒展效果。
2. 调整容器与指示器布局
- 确保外层容器的
overflow-hidden属性生效,避免动画过程中元素溢出 - 简化指示器的宽度计算,通过transform而非固定宽度调整位置,减少布局冲突
3. 优化动画关键帧
- 向左切换(Yearly→Monthly)时设置
transform-origin: right,向右切换(Monthly→Yearly)时设置transform-origin: left - 关键帧中同步控制
translateX和scaleX,模拟液态挤压的自然形变过程
完整修复代码
CSS 部分
body { background: rgb(147 51 234); } /* 统一动画类,通过transform-origin控制方向 */ .animate-liquid-slide { animation: liquid-slide 0.6s forwards cubic-bezier(0.4, 0, 0.2, 1); } @keyframes liquid-slide { 0% { transform: translateX(0) scaleX(1); } 25% { transform: translateX(0) scaleX(1.3); } 50% { transform: translateX(var(--slide-offset)) scaleX(1); } 100% { transform: translateX(var(--slide-offset)) scaleX(1); } }
HTML 部分
<script src="https://unpkg.com/petite-vue" defer init></script> <script src="https://cdn.tailwindcss.com"></script> <div v-scope="{ planRate: 'Yearly', prevRate: 'Yearly' }" class="flex justify-center items-center mt-8"> <div class="flex bg-gray-200/20 rounded-full overflow-hidden relative p-1 shadow"> <!-- 白色背景指示器 --> <div class="absolute top-0 left-0 h-full w-1/2 p-1 transition-all" :class="{ 'animate-liquid-slide': planRate !== prevRate }" :style="{ '--slide-offset': planRate === 'Yearly' ? '100%' : '0%', 'transform-origin': planRate === 'Yearly' ? 'left' : 'right', 'left': planRate === 'Yearly' ? '0' : 'auto', 'right': planRate === 'Yearly' ? 'auto' : '0' }" @animationstart="prevRate = planRate" > <div class="w-full h-full rounded-full bg-white"></div> </div> <!-- 切换按钮 --> <button type="button" class="text-sm font-medium rounded-full py-2 px-6 relative z-10 transition-colors duration-300" :class="planRate === 'Monthly' ? 'text-black' : 'text-white'" @click="planRate = 'Monthly'" > Monthly </button> <button type="button" class="text-sm font-medium rounded-full py-2 px-6 relative z-10 transition-colors duration-300" :class="planRate === 'Yearly' ? 'text-black' : 'text-white'" @click="planRate = 'Yearly'" > Yearly </button> </div> </div>
修复说明
- 使用CSS变量
--slide-offset动态控制滑动距离,配合transform-origin实现双向的挤压效果 - 动画采用
cubic-bezier曲线,让液态形变更自然 - 通过
prevRate变量触发动画,确保每次切换都能正确执行动画序列
内容的提问来源于stack exchange,提问作者tmarois
相关产品推荐
相关产品推荐

