一行代码实现黑夜主题&过渡

18 阅读1分钟

`

```<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/darkreader@4.9.58/darkreader.min.js"></script>
</head>

<body>
    <h1>Hello World</h1>
    <!-- 切换按钮 -->
    <button onclick="toggleDarkMode(event)">切换主题</button>
    <script>
        // Dark Reader 配置
        const customFetch = (url) => {
            return fetch(url, {
                mode: 'no-cors',
                credentials: 'include'
            });
        };
        DarkReader.setFetchMethod(customFetch);
        const darkReaderConfig = {
            brightness: 100,
            contrast: 100,
            sepia: 10
        };

        // 初始化主题状态
        let isDarkMode = JSON.parse(localStorage.getItem('darkMode')) || false;
        
        // 初始化主题
        if (isDarkMode) {
            DarkReader.enable(darkReaderConfig);
        }
        
        // 主题切换逻辑
        function toggleTheme(currentIsDarkMode) {
            if (currentIsDarkMode) {
                DarkReader.disable();
                localStorage.setItem('darkMode', false);
            } else {
                DarkReader.enable(darkReaderConfig);
                localStorage.setItem('darkMode', true);
            }
        }
        
        function toggleDarkMode(event) {
            const currentIsDarkMode = JSON.parse(localStorage.getItem('darkMode')) || false;

            // 使用View Transitions API
            if (!document.startViewTransition) {
                // 如果不支持View Transitions API,则使用普通切换
                toggleTheme(currentIsDarkMode);
                return;
            }

            // 获取点击位置
            const x = event.clientX;
            const y = event.clientY;

            // 计算扩散半径
            const endRadius = Math.hypot(
                Math.max(x, innerWidth - x),
                Math.max(y, innerHeight - y)
            );

            // 开始过渡动画
            const transition = document.startViewTransition(() => {
                toggleTheme(currentIsDarkMode);
            });

            // 设置动画
            transition.ready.then(() => {
                if (currentIsDarkMode) {
                    // 黑夜 → 白天:黑色收缩效果
                    // 让old元素(黑色)在上层,从全屏收缩到点击位置
                    document.documentElement.style.setProperty('--old-z-index', '2');
                    document.documentElement.style.setProperty('--new-z-index', '1');
                    
                    document.documentElement.animate(
                        {
                            clipPath: [
                                `circle(${endRadius}px at ${x}px ${y}px)`,
                                `circle(0px at ${x}px ${y}px)`
                            ]
                        },
                        {
                            duration: 450,
                            easing: 'ease-in-out',
                            pseudoElement: '::view-transition-old(root)'
                        }
                    );
                } else {
                    // 白天 → 黑夜:黑色扩散效果
                    // 让new元素(黑色)在上层,从点击位置扩散到全屏
                    document.documentElement.style.setProperty('--old-z-index', '1');
                    document.documentElement.style.setProperty('--new-z-index', '2');
                    
                    document.documentElement.animate(
                        {
                            clipPath: [
                                `circle(0px at ${x}px ${y}px)`,
                                `circle(${endRadius}px at ${x}px ${y}px)`
                            ]
                        },
                        {
                            duration: 450,
                            easing: 'ease-in-out',
                            pseudoElement: '::view-transition-new(root)'
                        }
                    );
                }
            });
        }
    </script>
    <style>
        /* 添加View Transitions相关样式 */
        ::view-transition-new(root),
        ::view-transition-old(root) {
            animation: none;
            mix-blend-mode: normal;
        }

        /* 确保主题切换时内容不会闪烁 */
        ::view-transition-old(root) {
            z-index: var(--old-z-index, 1);
        }

        ::view-transition-new(root) {
            z-index: var(--new-z-index, 2);
        }

        /* 防止过渡期间的内容跳动 */
        ::view-transition-group(root) {
            isolation: auto;
        }

        /* 确保动画平滑进行 */
        html {
            view-transition-name: root;
        }
    </style>
</body>

</html>