使用vue3、tailwind3
目标:单击“每年”或“每月”时,白色背景会向新选择的选项方向过渡,并具有液体/挤压运动,因此感觉更流畅
挑战: 从每月到每年看起来很棒!但从每年到每月却不会,即使使用相同的动画和相反的过渡也不会产生相同的效果。
每年到每月,你可以先看看它是如何从右侧走出来的:
过渡不会挤压:(观察是因为 1.5 的scaleX,但是它偏离右侧的小故障弄乱了这一点)
每月到每年有正确的挤压过渡:
您可能会说,为什么不在原点右侧执行scaleX 1.5,但它会出现故障并产生更糟糕的效果。除了切断侧面之外,这个 CSS 是唯一看起来有点正常的方式。
body {
background: rgb(147 51 234)
}
.animate-slide-and-squeeze {
animation: slide-and-squeeze 0.7s forwards;
}
.animate-slide-and-expand {
animation: slide-and-expand 0.4s forwards;
}
@keyframes slide-and-squeeze {
0% {
transform-origin: left;
transform: translateX(0) scaleX(1.5);
}
50% {
transform-origin: left;
transform: translateX(100%) scaleX(1);
}
100% {
transform-origin: left;
transform: translateX(100%) scaleX(1);
}
}
@keyframes slide-and-expand {
0% {
transform-origin: right;
transform: translateX(100%) scaleX(1);
}
50% {
transform-origin: right;
transform: translateX(100%) scaleX(1);
}
100% {
transform-origin: right;
transform: translateX(0) scaleX(1);
}
}
<script src="https://unpkg.com/petite-vue" defer init></script>
<script src="https://cdn.tailwindcss.com"></script>
<div v-scope="{ planRate: 'Yearly' }" class="flex justify-center items-center mt-8">
<div class="flex bg-gray-200/[0.2] rounded-full transition-all duration-500 overflow-hidden relative p-1 shadow">
<div class="absolute top-0 left-0 h-full transition-all ease-in-out duration-500 transform p-1" :class="{
'animate-slide-and-squeeze': planRate === 'Yearly',
'animate-slide-and-expand ': planRate === 'Monthly'
}" :style="{ width: planRate === 'Monthly' ? 'calc(50% + 0.5rem)' : 'calc(50%)'}">
<div class="w-full h-full rounded-full bg-white indicator"></div>
</div>
<button type="button" class="text-sm font-medium rounded-full py-2 px-6 relative z-10 transition-colors duration-200" :class="planRate === 'Monthly' ? 'text-black delay-200' : '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-500 delay-100" :class="planRate === 'Yearly' ? 'text-black' : 'text-white'" @click="planRate = 'Yearly'">Yearly</button>
</div>
</div>
所以这里的问题实际上是根据状态设置的宽度。由于 Vue 重新渲染的方式,
width
被添加并且转换正在运行,但它们不是同时发生的。所以,它从边缘延伸出来,宽度得到调整,然后动画运行。
这里的技巧是将宽度添加到动画中,以便它可以在调整变换的同时调整宽度。
我对你的方式做了一些调整——有些只是删除了不需要的顺风类。也就是说,我相信我已经按照您想要的方式工作了。动画看起来前后都是一样的。您可能需要设置一些与初始状态样式相匹配的初始 CSS。所以它不会在加载时动画。
body {
background: rgb(147 51 234)
}
.animate-slide-and-squeeze {
animation: slide-and-squeeze 0.7s forwards;
}
.animate-slide-and-expand {
animation: slide-and-expand 0.7s forwards;
}
@keyframes slide-and-squeeze {
0% {
transform-origin: left;
transform: translateX(0) scaleX(1.5);
}
50% {
transform-origin: left;
transform: translateX(100%) scaleX(1);
}
100% {
transform-origin: left;
transform: translateX(100%) scaleX(1);
}
}
@keyframes slide-and-expand {
0% {
transform-origin: right;
transform: translateX(100%) scaleX(1.5);
width: 50%;
}
50% {
transform-origin: right;
transform: translateX(0) scaleX(1);
width: calc(50% + 0.5rem);
}
100% {
transform-origin: right;
transform: translateX(0) scaleX(1);
width: calc(50% + 0.5rem);
}
}
<script src="https://unpkg.com/petite-vue" defer init></script>
<script src="https://cdn.tailwindcss.com"></script>
<div v-scope="{ planRate: 'Yearly' }" class="flex justify-center items-center mt-8">
<div class="flex bg-gray-200/[0.2] rounded-full transition-all duration-500 overflow-hidden relative p-1 shadow">
<div class="absolute top-0 left-0 h-full p-1 w-1/2" :class="{
'animate-slide-and-squeeze': planRate === 'Yearly',
'animate-slide-and-expand ': planRate === 'Monthly'
}">
<div class="w-full h-full rounded-full bg-white"></div>
</div>
<button type="button" class="flex-1 text-sm font-medium rounded-full py-2 px-6 relative z-10 transition-colors duration-200" :class="planRate === 'Monthly' ? 'text-black delay-200' : 'text-white'" @click="planRate = 'Monthly'">Monthly</button>
<button type="button" class="flex-1 text-sm font-medium rounded-full py-2 px-6 relative z-10 transition-colors duration-200" :class="planRate === 'Yearly' ? 'text-black' : 'text-white'" @click="planRate = 'Yearly'">Yearly</button>
</div>
</div>
注意:为了演示,我使用了
petite-vue
,但在 Vue 3 中也是一样的。