网站LOGO
白雾茫茫丶
页面加载中
12月22日
网站LOGO 白雾茫茫丶
记录学习、生活和有趣的事
菜单
  • 白雾茫茫丶
    记录学习、生活和有趣的事
    用户的头像
    首次访问
    上次留言
    累计留言
    我的等级
    我的角色
    打赏二维码
    打赏博主
    Next.js 实战 (三):优雅的实现暗黑主题模式
    点击复制本页信息
    微信扫一扫
    文章二维码
    文章图片 文章标题
    创建时间
  • 一 言
    确认删除此评论么? 确认
  • 本弹窗介绍内容来自,本网站不对其中内容负责。
    • 复制图片
    • 复制图片地址
    • 百度识图
    按住ctrl可打开默认菜单

    Next.js 实战 (三):优雅的实现暗黑主题模式

    谢明伟 · 原创 ·
    前端开发Next 实战 · ReactNext
    共 4156 字 · 约 1 分钟 · 123

    前言

    Next.js 中要实现暗黑模式,需要用到一个库:next-themes,它可以帮助我们很轻易地实现暗黑模式切换。

    具体步骤

    1. 安装 next-themes 依赖:

      powershell 代码:
      pnpm add next-themes
    2. 新增 /components/ThemeProvider/index.tsx 文件:

      html 代码:
      'use client';
      
      import { ThemeProvider as NextThemesProvider } from 'next-themes';
      import * as React from 'react';
      
      export default function ThemeProvider({ children, ...props }: React.ComponentProps<typeof NextThemesProvider>) {
        return <NextThemesProvider {...props}>{children}</NextThemesProvider>;
      }
    3. /app/layout.tsx 文件中注入 ThemeProvider :

      html 代码:
      import { ThemeProvider } from "@/components/theme-provider"
      
      export default function RootLayout({ children }: RootLayoutProps) {
        return (
       <>
         <html lang="en" suppressHydrationWarning>
           <head />
           <body>
             <ThemeProvider
               attribute="class"
               defaultTheme="system"
               enableSystem
               disableTransitionOnChange
             >
               {children}
             </ThemeProvider>
           </body>
         </html>
       </>
        )
      }
    4. 新增 /components/ThemeModeButton/index.tsx 主题切换组件:

      html 代码:
      'use client';
      
      import { Moon, Sun } from 'lucide-react';
      import { useTheme } from 'next-themes';
      
      import { Button } from '@/components/ui/button';
      
      export default function ThemeModeButton() {
        const { theme, setTheme } = useTheme();
      
        return (
       <Button variant="ghost" size="icon" onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
         <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
         <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
         <span className="sr-only">Toggle theme</span>
       </Button>
        );
      }

    过渡动画

    1. 如果你想加入过渡动画,可以把代码改成这样:

      html 代码:
      'use client';
      
      import { Moon, Sun } from 'lucide-react';
      import { useTheme } from 'next-themes';
      
      import { Button } from '@/components/ui/button';
      
      export default function ThemeModeButton() {
        const { theme, setTheme } = useTheme();
      
        // 判断是否支持 startViewTransition API
        const enableTransitions = () =>
       'startViewTransition' in document && window.matchMedia('(prefers-reduced-motion: no-preference)').matches;
      
        // 切换动画
        async function toggleDark({ clientX: x, clientY: y }: MouseEvent) {
       const isDark = theme === 'dark';
      
       if (!enableTransitions()) {
         setTheme(theme === 'light' ? 'dark' : 'light');
         return;
       }
      
       const clipPath = [
         `circle(0px at $px ${y}px)`,
         `circle(${Math.hypot(Math.max(x, innerWidth - x), Math.max(y, innerHeight - y))}px at $px ${y}px)`,
       ];
      
       await document.startViewTransition(async () => {
         setTheme(theme === 'light' ? 'dark' : 'light');
       }).ready;
      
       document.documentElement.animate(
         { clipPath: !isDark ? clipPath.reverse() : clipPath },
         {
           duration: 300,
           easing: 'ease-in',
           pseudoElement: `::view-transition-${!isDark ? 'old' : 'new'}(root)`,
         },
       );
        }
      
        return (
       <Button variant="ghost" size="icon" onClick={toggleDark}>
         <Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
         <Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
         <span className="sr-only">Toggle theme</span>
       </Button>
        );
      }
    2. /app/glocals.css 文件中加入过渡样式:

      css 代码:
      ::view-transition-old(root),
      ::view-transition-new(root) {
        animation: none;
        mix-blend-mode: normal;
      }
      
      ::view-transition-old(root),
      .dark::view-transition-new(root) {
        z-index: 1;
      }
      
      ::view-transition-new(root),
      .dark::view-transition-old(root) {
        z-index: 9999;
      }

    使用方法

    在需要的位置引入组件:

    html 代码:
    import ThemeModeButton from '@/components/ThemeModeButton';
    
    <ThemeModeButton />

    最终效果

    声明:本文由 谢明伟(博主)原创,依据 CC-BY-NC-SA 4.0 许可协议 授权,转载请注明出处。

    还没有人喜爱这篇文章呢

    我要发表评论 我要发表评论
    博客logo 白雾茫茫丶 记录学习、生活和有趣的事 51统计 百度统计
    MOEICP 萌ICP备20236860号 ICP 粤ICP备2023007649号 ICP 粤公网安备44030402006402号

    💻️ 谢明伟 一天前 在线

    🕛

    本站已运行 2 年 355 天 15 小时 1 分

    🌳

    自豪地使用 Typecho 建站,并搭配 MyLife 主题
    白雾茫茫丶. © 2022 ~ 2024.
    网站logo

    白雾茫茫丶 记录学习、生活和有趣的事