如何在React中使用Tailwind CSS实现带动画的标签切换效果?
The issue with your current implementation is that you're applying the sliding transform directly to the button container—this makes the entire container (including the text) move. To fix this, we need to separate the animated background slider from the interactive buttons. The slider will sit behind the buttons, moving independently, while the buttons stay in place to handle clicks.
Here's the revised code that achieves the desired effect:
import React from 'react' import clsx from 'clsx' export const Modal = () => { const [theme, setTheme] = React.useState<'light' | 'dark' | 'system'>('light') // Calculate slider position based on active theme const sliderPosition = clsx({ 'left-2': theme === 'light', 'left-[calc(33.333%+2px)]': theme === 'dark', 'left-[calc(66.666%+2px)]': theme === 'system', }) return ( <div className="relative flex mx-2 mt-2 rounded-md bg-blue-gray-100"> {/* Animated background slider - sits behind buttons */} <div className={clsx( 'absolute top-2 bottom-2 rounded-md bg-white transition-all duration-300 ease-out', sliderPosition, 'w-[calc(33.333%-4px)]' // Account for left/right margins )} /> {/* Light Theme Button */} <div className="flex-1 py-1 my-2 ml-2 text-center rounded-md"> <button className={clsx( 'w-full text-sm cursor-pointer select-none focus:outline-none', { 'font-bold text-blue-gray-900': theme === 'light', 'text-blue-gray-600': theme !== 'light', } )} onClick={() => setTheme('light')} > Light </button> </div> {/* Dark Theme Button */} <div className="flex-1 py-1 my-2 ml-2 text-center rounded-md"> <button className={clsx( 'w-full text-sm cursor-pointer select-none focus:outline-none', { 'font-bold text-blue-gray-900': theme === 'dark', 'text-blue-gray-600': theme !== 'dark', } )} onClick={() => setTheme('dark')} > Dark </button> </div> {/* System Theme Button */} <div className="flex-1 py-1 my-2 mr-2 text-center rounded-md"> <button className={clsx( 'w-full text-sm cursor-pointer select-none focus:outline-none', { 'font-bold text-blue-gray-900': theme === 'system', 'text-blue-gray-600': theme !== 'system', } )} onClick={() => setTheme('system')} > System </button> </div> </div> ) }
Key Changes Explained:
- Relative Parent Container: Added
relativeto the outer<div>so the absolute-positioned slider can be positioned relative to it. - Independent Slider Element: Created a separate slider
<div>that sits behind the buttons. It usesabsolutepositioning, so it doesn't affect the layout of the buttons. - Dynamic Slider Position: Calculated the slider's
leftposition using percentage values (33.333% per tab) to account for the flex-1 widths. Thecalc()adjusts for theml-2margins on the buttons. - Cleaned Up Button Containers: Removed the
bg-whiteand transform styles from the button containers—now the slider provides the selected background effect, while buttons only handle text styling and clicks. - Smooth Transition: Used
transition-all duration-300 ease-outon the slider for a smooth animated shift between tabs.
This setup keeps your buttons as the interactive elements while letting the background slider move independently, so your text stays in place during the animation.
内容的提问来源于stack exchange,提问作者deadcoder0904
相关产品推荐
相关产品推荐

