Element-Plus官网黑夜模式切换动画效果的实现

前言

在使用Element-Plus时,发现有两个很有趣的效果,一个是header的背景模糊效果,另一个是黑夜模式切换动画,在此我们先来研究一下黑夜模式切换动画效果是如何实现的。

代码

html部分

<div id="box">
  <input type="checkbox" id="checkBox">
  <label for="checkBox"></label>
</div>
<iframe src="https://www.baidu.com"></iframe>

css部分

        html,
        body {
            background-color: #fff;
            height: 100vh;
            display: flex;
            flex-direction: column;
        }

        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        iframe {
            flex: 1;
        }

        html.dark {
            filter: invert(1) hue-rotate(180deg);

            img,
            video,
            .avatar,
            .image,
            .thumb,
            #box {
                filter: invert(1) hue-rotate(180deg);
            }
        }

        ::view-transition-old(root),
        ::view-transition-new(root) {
            animation: none;
            mix-blend-mode: normal;
        }

        ::view-transition-old(root) {
            z-index: 1;
        }

        ::view-transition-new(root) {
            z-index: 2147483646;
        }

        html.dark::view-transition-old(root) {
            z-index: 2147483646;
        }

        html.dark::view-transition-new(root) {
            z-index: 1;
        }

        #checkBox {
            opacity: 0;
            width: 0;
        }

        #box>label {
            display: block;
            width: 70px;
            height: 30px;
            border-radius: 30px;
            border: 1px solid #dcdfe6;
            background-color: #f2f2f2;
            position: relative;
            transition: all 0.3s;
            display: flex;
            align-items: center;
        }

        #checkBox:checked+label {
            background-color: #2c2c2c;
            border: 1px solid #4c4d4f;
        }

        #box>label::before {
            content: '🌞';
            position: absolute;
            left: 0;
            line-height: 25px;
            font-size: 18px;
            text-align: center;
            background-color: #fff;
            border-radius: 18px;
            border: 1px solid #dcdfe6;
            transition: all 0.3s;
        }

        #checkBox:checked+label::before {
            display: none;
        }

        #checkBox+label::after {
            content: '🌙';
            position: absolute;
            display: none;
            left: 0;
            line-height: 25px;
            font-size: 18px;
            text-align: center;
            background-color: #141414;
            border-radius: 18px;
            border: 1px solid #4c4d4f;
            transition: all 0.3s;
        }

        #checkBox:checked+label::after {
            left: calc(100% - 26px);
            right: 0;
            display: block;
            border: 1px solid #4c4d4f;
            transition: all 0.3s;
        }

js部分

        document.getElementById('checkBox').addEventListener('click', function (event) {
                toggleTheme(event);
            });

        // 切换主题
        function toggleTheme(event) {
            // 检查浏览器是否支持 View Transition API
            if (!document.startViewTransition) {
                // 不支持则直接切换主题,不添加动画
                document.documentElement.classList.toggle('dark')
                return
            }
            const transition = document.startViewTransition(() => {
                document.documentElement.classList.toggle('dark')
            })

            transition.ready.then(() => {
                const { clientX, clientY } = event

                const endRadius = Math.hypot(Math.max(clientX, innerWidth - clientX), Math.max(clientY, innerHeight - clientY))

                const clipPath = [`circle(0px at ${clientX}px ${clientY}px)`, `circle(${endRadius}px at ${clientX}px ${clientY}px)`]

                const isDark = document.documentElement.classList.contains('dark')

                document.documentElement.animate(
                    {
                        clipPath: isDark ? clipPath.reverse() : clipPath
                    },
                    {
                        duration: 450,
                        easing: 'ease-in',
                        pseudoElement: isDark ? '::view-transition-old(root)' : '::view-transition-new(root)'
                    }
                )
            })
        }

上面切换动画效果的实现用到了View Transition API,同时,用到了filter: invert(1) hue-rotate(180deg)来实现滤镜反色,以确保不同主题下背景和内容之间的对比度。

大家快去实现一下吧!

人心如良苗,得养乃滋长。苗以泉水灌,心以理义养。一日不读书,胸臆无佳想。一月不读书,耳目失精爽。

参考文章:来实现一下 element-plus 中的主题切换动画 --掘金

posted @ 2025-06-25 19:40  悦动代码  阅读(329)  评论(0)    收藏  举报