【问题标题】:Make animated tabs in Tailwind CSS?在 Tailwind CSS 中制作动画标签?
【发布时间】:2021-05-22 13:39:15
【问题描述】:

我想制作一个像这样的动画标签:

我正在使用带有 Tailwind 的 React。这是我的代码:

import React from 'react'
import clsx from 'clsx'

export const Modal = () => {
  const [theme, setTheme] = React.useState<'light' | 'dark' | 'system'>('light')
  return (
    <div className="flex mx-2 mt-2 rounded-md bg-blue-gray-100">
      <div
        className={clsx('flex-1 py-1 my-2 ml-2 text-center rounded-md', {
          'bg-white': theme === 'light',
          'transition duration-1000 ease-out transform translate-x-10':
            theme !== 'light',
        })}
      >
        <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>
      <div
        className={clsx('flex-1 py-1 my-2 ml-2 text-center rounded-md', {
          'bg-white': theme === 'dark',
        })}
      >
        <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>
      <div
        className={clsx('flex-1 py-1 my-2 mr-2 text-center rounded-md', {
          'bg-white': theme === 'system',
        })}
      >
        <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>
  )
}

但它看起来像:

当主题不是light 时,我使用translate-x-10,因此文本也会移动。

我希望 UI 与上面的完全一样,同时仍然使用实际选项卡的按钮。

最小代码沙盒 → https://codesandbox.io/s/mobx-theme-change-n1nvg?file=/src/App.tsx

我该怎么做?

【问题讨论】:

  • 将动画移动到&lt;span&gt; 并应用translate-x-10 怎么样?
  • @markmead span 应该在哪里?我在button 上方尝试过,没有包围它,但我什么也没看到。
  • @markmead 这里是代码框 → codesandbox.io/s/mobx-theme-change-n1nvg

标签: html css reactjs css-transitions tailwind-css


【解决方案1】:

事实证明,纯 Tailwind 是可能的。

tailwind.config.js

module.exports = {
    theme: {
        extend: {
            translate: {
                200: '200%',
            },
        },
    },
}

App.tsx

import * as React from "react"
import { observer } from "mobx-react"
import clsx from "clsx"

import { useStore } from "./context"

const AppTheme = observer(() => {
    const {
        theme: { app },
        updateTheme,
    } = useStore()

    return (
        <>
            <div className="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left">
                <div className="mt-2">
                    <h4 className="text-xl font-bold text-gray-800">Background</h4>
                </div>
            </div>

            <div className="relative mx-2 mt-2 rounded-md bg-gray-100">
                <div
                    id="slider"
                    className={clsx(
                        "absolute inset-y-0 w-1/3 h-full px-4 py-1 transition-transform transform",
                        {
                            "translate-x-0": app === "light",
                            "translate-x-full": app === "dark",
                            "translate-x-200": app === "system",
                        },
                    )}
                    style={
                        app === "system"
                            ? {
                                    transform: "translateX(200%)", // if you added `translate-x-200` to `tailwind.config.js` then you can remove the `style` tag completely
                              }
                            : {}
                    }
                >
                    <div
                        className={clsx(
                            "w-full h-full bg-white rounded-md",
                            {
                                active: app === "light",
                                "bg-gray-600": app === "dark",
                            },
                            {
                                // needs to be separate object otherwise dark/light & system keys overlap resulting in a visual bug
                                ["bg-gray-600"]: app === "system",
                            },
                        )}
                    ></div>
                </div>
                <div className="relative flex w-full h-full">
                    <button
                        tabIndex={0}
                        className={clsx(
                            "py-1 my-2 ml-2 w-1/3 text-sm cursor-pointer select-none focus:outline-none",
                            {
                                active: app === "light",
                                "font-bold text--gray-900": app === "light",
                                "text--gray-600": app !== "light",
                            },
                        )}
                        onKeyUp={(event: React.KeyboardEvent<HTMLElement>) => {
                            if (event.key === "Tab")
                                updateTheme({
                                    app: "light",
                                })
                        }}
                        onClick={() => {
                            updateTheme({
                                app: "light",
                            })
                        }}
                    >
                        Light
                    </button>
                    <button
                        tabIndex={0}
                        className={clsx(
                            "py-1 my-2 ml-2 w-1/3 text-sm cursor-pointer select-none focus:outline-none",
                            {
                                active: app === "dark",
                                "font-bold text-white": app === "dark",
                                "text--gray-600": app !== "dark",
                            },
                        )}
                        onKeyUp={(event: React.KeyboardEvent<HTMLElement>) => {
                            if (event.key === "Tab")
                                updateTheme({
                                    app: "dark",
                                })
                        }}
                        onClick={() => {
                            updateTheme({
                                app: "dark",
                            })
                        }}
                    >
                        Dark
                    </button>
                    <button
                        tabIndex={0}
                        className={clsx(
                            "py-1 my-2 ml-2 w-1/3 text-sm cursor-pointer select-none focus:outline-none",
                            {
                                active: app === "system",
                                "font-bold text-white": app === "system",
                                "text--gray-600": app !== "system",
                            },
                        )}
                        onKeyUp={(event: React.KeyboardEvent<HTMLElement>) => {
                            if (event.key === "Tab")
                                updateTheme({
                                    app: "system",
                                })
                        }}
                        onClick={() => {
                            updateTheme({
                                app: "system",
                            })
                        }}
                    >
                        System
                    </button>
                </div>
            </div>
        </>
    )
})

export default observer(function App() {
    return <AppTheme />
})

代码沙盒 → https://codesandbox.io/s/mobx-theme-change-animated-18gc6?file=/src/App.tsx

想知道为什么它不在 Codesandbox 上制作动画,但它在本地工作。也许是 Codesandbox 错误 :)

【讨论】:

    【解决方案2】:

    你可以很容易地做这个动画,你需要在包含三个按钮的父元素中添加另一个标签元素。

    因此,该元素将跟踪哪个按钮处于活动状态,并将根据其宽度移动。

    例如,如果第一个按钮是活动的,这个元素根本不会被翻译,因为它是第一个元素,所以位置会是0。

    这个会做动画的元素会有绝对定位,像这样:

    .tab-item-animate {
      position: absolute;
      top: 6px;
      left: 6px;
      width: calc(100% - 12px);
      height: 32px;
      transform-origin: 0 0;
      transition: transform 0.25s;
    }
    

    第一个按钮处于活动状态:

    .tabs .tabs-item:first-child.active ~ .tab-item-animate {
      transform: translateX(0) scaleX(0.333);
    }
    

    第二个按钮激活:

    .tabs .tabs-item:nth-child(2).active ~ .tab-item-animate {
      transform: translateX(33.333%) scaleX(0.333);
    }
    

    第三个​​按钮激活:

    .tabs .tabs-item:nth-child(3).active ~ .tab-item-animate {
      transform: translateX(33.333% * 2) scaleX(0.333);
    }
    

    我没有太多使用 Tailwind 的经验,但我不确定你是否可以用它来管理整个事情(也许你可以用我的代码做一些其他操作,只用 Tailwind 来做)。

    我为此添加了一个单独的 CSS 文件,我提供了一个演示,基于您共享的代码:

    tabs animated link

    PS:我对你的 HTML 结构做了一点改动,你不需要在每个按钮上方添加另一个 div,没有必要。

    【讨论】:

    • 该死的,非常感谢 Teuta。我也认为它不能用 Tailwind 来完成,但我从来没有尝试过动画任何东西所以不确定:)
    • 嘿Teuta,你是怎么决定top & left 成为6px & height 成为32px & width 成为calc(100% - 12px)?你是在 DevTools 中检查了computed 属性还是通过添加font-size + margin + padding 来计算的?另外,我不明白scaleX 的必要性?如果你能在帖子中解释它会很高兴:)
    • 再次感谢您。我之所以问,是因为当我向其中添加 focus-ring 时计算有点偏离,但我会从这里开始管理。再次感谢,感谢您的帮助:)
    • Oye Tueta,只是想让您知道 Tailwind 完全可以实现,我在下面发布了解决方案。
    • @deadcoder0904,哇,太棒了!我很高兴看到您找到了使用 Tailwind 的解决方案! :)
    猜你喜欢
    • 2021-09-11
    • 2022-01-06
    • 2022-11-04
    • 2021-12-21
    • 1970-01-01
    • 2016-05-09
    • 2021-06-05
    • 2021-01-15
    • 2023-03-16
    相关资源
    最近更新 更多