单标签下的日间与黑夜模式切换按钮效果

2023-07-19
20577 分钟
...

前不久,在网上看到这么一张非常有趣的图:

想必很多同学都看到这张图,是一个开发小哥被一个日间/黑夜模式切换按钮效果逼疯的视频。

其最终效果大致如下:

原完整代码在这里:Night && Day Toggle ☀️/🌙 [Completed It!]

原效果用了大量 HTML 标签,大量 SVG 元素以及 350 行的 CSS 完成的上述效果。

而本文,我们将尝试优化一下代码,尝试仅仅使用一个标签,完成上述效果。

当然,首先,我们需要一个标签:



HTML

复制代码

`<div></div>` 

接下来,在单个标签内,我们一步一步来实现这个效果。

拟态阴影

先把整个按钮的形状确定下来,我们需要这样一个整体的拟物形状:

可以看到,这个造型非常的立体。这里的核心是 -- 利用阴影,构建拟态效果

怎么操作呢?其原理就在于,使用两组阴影,使用两个相反的方向,使用两组对比明显的颜色值,来实现凹凸效果。

我们需要使用盒子的内阴影实现。

看个例子:



HTML

复制代码

`<div>浮雕阴影</div>
<div>浮雕阴影</div>` 


CSS

复制代码

`div {
 width: 120px;
 height: 120px;
 background: #e9ecef;
 color: #333;
 box-shadow:
 7px 7px 12px rgba(0, 0, 0, .4),
 -7px -7px 12px rgba(255, 255, 255, .9);
}

div:nth-child(2) {
 box-shadow:
 inset -7px -7px 12px rgba(255, 255, 255, .9),
 inset 7px 7px 12px rgba(0, 0, 0, .4);
}` 

这样,就可以得到拟态风格的按钮,如下图所示,左凸右凹:

借鉴这个方式,我们很快就能得到整个按钮的外形代码:



CSS

复制代码

`body {
 width: 100%;
 height: 100%;
 background: #d9deea;
}
div {
 width: 220px;
 height: 90px;
 border-radius: 90px;
 box-shadow: 
 0 -3px 4px #999,
 inset 0 3px 5px #333,
 0 4px 4px #ffe,
 inset 0 -3px 5px #ddd;
}` 

这样,整个外框就实现了:

日间模式的实现

好,接下来,我们来实现日间模式,其整个效果如下:

仔细观察上述图形,除了外框外,主要还有几大部分:

  1. 一个圆形太阳
  2. 太阳的光晕
  3. 云朵效果

发现了吗?它们都是圆形!而在 CSS 中,能够利用单个属性构建多个圆形的方式有非常多种:

  1. box-shadow
  2. filter: drop-shadow()
  3. background 渐变

并且,上面我们只使用了 div 本身,还有两个伪元素没有使用。我们需要充分把这两个伪元素利用起来。这里,我们这样分工一下:

  1. 伪元素 ::before: 用于实现太阳本身
  2. 伪元素 ::after:用于实现太阳的光晕及云朵效果

我们一步一步来。

利用伪元素 ::before: 实现太阳本身

这个还是非常好理解的,直接上代码:



CSS

复制代码

`div::before {
 content: "";
 position: absolute;
 width: 75px;
 height: 75px;
 border-radius: 50%;
 background: #e9cb50;
 inset: 7.5px;
 box-shadow: 
 0 0 5px #333,
 inset 2px 2px 3px #f8f4e4,
 inset -2px -2px 3px #665613;
}` 

核心就是利用伪元素,再生成一个圆,再添加相应的阴影即可,效果如下:

利用伪元素 ::after: 实现太阳的光晕及云朵效果

注意!这里是本文最为关键的地方。如何利用剩下一个伪元素实现太阳的光晕及云朵效果?

这里就需要利用到 box-shadow 可以复制自身的技巧。在非常多篇的文章中也有反复提到过。

譬如,当我们想实现一朵云朵,像是这样:

使用 box-shadow 即可轻松实现:



HTML

复制代码

`<div></div>` 


CSS

复制代码

`div{
 width:100px;
 height:100px;
 margin:50px auto;
 background:#999;
 border-radius:50%;
 box-shadow:
 120px 0px 0 -10px #999,
 95px 20px 0 0px #999,
 30px 30px 0 -10px #999,
 90px -20px 0 0px #999,
 40px -40px 0 0px #999;
}` 

通过动图,感受一下是什么意思:

嘿,这个云朵不是和我们效果中的云朵非常类似吗?只需要调整一些位置,利用 overflow: hidden 裁剪掉多余部分即可。

光圈其实也是同理,这里,利用 ::after 伪元素,生成一个圆,利用多重 box-shadow,生成光晕和云朵!

代码如下:



CSS

复制代码

`&::after {
 content: "";
 position: absolute;
 width: 70px;
 height: 70px;
 inset: 10px;
 border-radius: 50%;
 box-shadow: 
 10px 60px 0 10px #fff,
 65px 60px 0 5px #fff,
 95px 70px 0 10px #fff,
 135px 45px 0 5px #fff,
 170px 35px 0 10px #fff,
 195px -5px 0 10px #fff,
 -10px 0 0 50px rgba(255, 255, 255, .2),
 15px 0 0 50px rgba(255, 255, 255, .15),
 40px 0 0 50px rgba(255, 255, 255, .21),
 10px 40px 0 10px #abc1d9,
 70px 35px 0 10px #abc1d9,
 95px 40px 0 10px #abc1d9,
 135px 20px 0 10px #abc1d9,
 155px 15px 0 10px #abc1d9,
 190px -20px 0 10px #abc1d9;
}` 

其核心,或者说费时间的地方在于调整每个 box-shadow 的位置和颜色,这样,我们就得到了完整的日间效果图:

夜间模式的实现

实现完日间效果,接下来,我们就需要实现夜间效果。其效果图如下:

为了实现最终的点击切换,我们可以把夜间效果下,按钮的样式,写在一个新的 class 内,这样,后面只需要在点击的过程中,去切换这个 class 即可。



HTML

复制代码

`<div class="active"></div>` 


CSS

复制代码

`div.active{
 ...
}` 

如上所示,我们接下来的工作就是寻找日间、夜间的差异点,将代码填入上述的 div.active 即可。

首先,太阳变成了月亮,位置进行了移动,颜色进行了变化,并且月亮上多出了一些陨石坑,当然,其本质还是圆形。

这些修改都非常简单,还是在原来的 ::before 基础上修改即可:



CSS

复制代码

`div.active{
 &::before {
 translate: 130px;
 background: 
 radial-gradient(circle at 50% 20px, #939aa5, #939aa5 6.5px, transparent 7px, transparent),
 radial-gradient(circle at 35% 45px, #939aa5, #939aa5 11.5px, transparent 12px, transparent),
 radial-gradient(circle at 72% 50px, #939aa5, #939aa5 8.5px, transparent 9px, transparent),
 radial-gradient(#cbcdda, #cbcdda);
 }
}` 

这里是非常好修改的,利用 radila-gradient(),也就是多重渐变,我们可以轻松的在一个元素内完成背景加上陨石坑的代码:

继续,夜间模式下,月亮也有光圈,代码是可以复用的,并且夜间模式没有了云朵,取而代之是星星。

星星看起来有点复杂,我们待会处理,这里仅仅需要把云朵部分的阴影颜色,设置为 transparent 即可。



CSS

复制代码

`div.active {
 &::after {
 transform: translate(130px);
 box-shadow: 
 10px 60px 0 10px transparent,
 65px 60px 0 5px transparent,
 95px 70px 0 10px transparent,
 135px 45px 0 5px transparent,
 170px 35px 0 10px transparent,
 195px -5px 0 10px transparent,
 10px 0 0 50px rgba(255, 255, 255, .2),
 -15px 0 0 50px rgba(255, 255, 255, .15),
 -40px 0 0 50px rgba(255, 255, 255, .21),
 10px 40px 0 10px transparent,
 70px 35px 0 10px transparent,
 95px 40px 0 10px transparent,
 135px 20px 0 10px transparent,
 155px 15px 0 10px transparent,
 190px -20px 0 10px transparent;
 }
}` 

效果如下:

为什么这里不是去掉云朵的代码,而是把云朵部分的阴影颜色,设置为 transparent 呢?这样做的原因是能够在切换过程中,得到更好的动画效果。

好!到这里,只剩下夜间模式下的星星和背景了,背景是非常好解决的,主要是星星,看原效果的动图,每一颗星星是带有棱角的,而这种不规则图案,确实是 CSS 最棘手的问题。

到这里,无奈退而求其次,考虑使用小圆点模拟星星效果。(没想到效果其实也很不错!)

那这个问题又变成了和月亮与陨石坑类似的问题了,都是圆形,那就非常好解决。

最终,考虑在 div 本身的背景之上,设置一个大背景 background-size: 200% 100%,这样,一半是日间的背景,一半是夜间的背景,在切换的过程中,只需要改变 background-position 即可。

这样一来,代码如下:



CSS

复制代码

`div {
 background: 
 radial-gradient(circle at 18% 20px, #fff, #fff 6px, transparent 7px, transparent),
 radial-gradient(circle at 35% 45px, #fff, #fff 1px, transparent 2px, transparent),
 radial-gradient(circle at 10% 70px, #fff, #fff 2.5px, transparent 3.5px, transparent),
 radial-gradient(circle at 25% 15px, #fff, #fff 3px, transparent 4px, transparent),
 radial-gradient(circle at 15% 50px, #fff, #fff 1.5px, transparent 2.5px, transparent),
 radial-gradient(circle at 30% 75px, #fff, #fff 5px, transparent 6px, transparent),
 radial-gradient(circle at 5% 30px, #fff, #fff 0.5px, transparent 1.5px, transparent),
 radial-gradient(circle at 25% 60px, #fff, #fff 0.5px, transparent 1.5px, transparent),
 radial-gradient(circle at 7% 35px, #fff, #fff 0.5px, transparent 1.5px, transparent),
 linear-gradient(90deg, #2b303e, #2b303e 50%, #5a81b4 50%, #5a81b4);
 background-repeat: no-repeat;
 background-size: 200% 100%;
 background-position: 100% 0;
}
div.active {
 background-position: 0 0;
}` 

这样,夜间效果也就完美实现了:

添加过渡效果以及切换效果

最后,只需要加上一些过渡效果以及点击切换时,元素样式类名变化的 JavaScript 代码即可。

完整的整个效果,代码如下:



HTML

复制代码

`<div id="g-btn"></div>` 


CSS

复制代码

`body {
 background: #d9deea;
}
div {
 position: relative;
 width: 220px;
 height: 90px;
 background: 
 radial-gradient(circle at 18% 20px, #fff, #fff 6px, transparent 7px, transparent),
 radial-gradient(circle at 35% 45px, #fff, #fff 1px, transparent 2px, transparent),
 radial-gradient(circle at 10% 70px, #fff, #fff 2.5px, transparent 3.5px, transparent),
 radial-gradient(circle at 25% 15px, #fff, #fff 3px, transparent 4px, transparent),
 radial-gradient(circle at 15% 50px, #fff, #fff 1.5px, transparent 2.5px, transparent),
 radial-gradient(circle at 30% 75px, #fff, #fff 5px, transparent 6px, transparent),
 radial-gradient(circle at 5% 30px, #fff, #fff 0.5px, transparent 1.5px, transparent),
 radial-gradient(circle at 25% 60px, #fff, #fff 0.5px, transparent 1.5px, transparent),
 radial-gradient(circle at 7% 35px, #fff, #fff 0.5px, transparent 1.5px, transparent),
 linear-gradient(90deg, #2b303e, #2b303e 50%, #5a81b4 50%, #5a81b4);
 background-repeat: no-repeat;
 background-size: 200% 100%;
 background-position: 100% 0;
 border-radius: 90px;
 box-shadow: 
 0 -3px 4px #999,
 inset 0 3px 5px #333,
 0 4px 4px #ffe,
 inset 0 -3px 5px #ddd;
 cursor: pointer;
 overflow: hidden;
 transition: .5s all;
 &::before,
 &::after {
 content: "";
 position: absolute;
 transition: .5s all;
 }
 &::before {
 width: 75px;
 height: 75px;
 border-radius: 50%;
 background: #e9cb50;
 inset: 7.5px;
 box-shadow: 
 0 0 5px #333,
 inset 2px 2px 3px #f8f4e4,
 inset -2px -2px 3px #665613;
 z-index: 1;
 }
 &::after {
 width: 70px;
 height: 70px;
 inset: 10px;
 border-radius: 50%;
 box-shadow: 
 10px 60px 0 10px #fff,
 65px 60px 0 5px #fff,
 95px 70px 0 10px #fff,
 135px 45px 0 5px #fff,
 170px 35px 0 10px #fff,
 195px -5px 0 10px #fff,
 -10px 0 0 50px rgba(255, 255, 255, .2),
 15px 0 0 50px rgba(255, 255, 255, .15),
 40px 0 0 50px rgba(255, 255, 255, .21),
 10px 40px 0 10px #abc1d9,
 70px 35px 0 10px #abc1d9,
 95px 40px 0 10px #abc1d9,
 135px 20px 0 10px #abc1d9,
 155px 15px 0 10px #abc1d9,
 190px -20px 0 10px #abc1d9;
 }
}
div:hover::before {
 filter: contrast(90%) brightness(110%);
 scale: 1.05;
}
div.active {
 background-position: 0 0;
 

 
 &::before {
 translate: 130px;
 background: 
 radial-gradient(circle at 50% 20px, #939aa5, #939aa5 6.5px, transparent 7px, transparent),
 radial-gradient(circle at 35% 45px, #939aa5, #939aa5 11.5px, transparent 12px, transparent),
 radial-gradient(circle at 72% 50px, #939aa5, #939aa5 8.5px, transparent 9px, transparent),
 radial-gradient(#cbcdda, #cbcdda);
 }
 &::after {
 transform: translate(130px);
 box-shadow: 
 10px 60px 0 10px transparent,
 65px 60px 0 5px transparent,
 95px 70px 0 10px transparent,
 135px 45px 0 5px transparent,
 170px 35px 0 10px transparent,
 195px -5px 0 10px transparent,
 10px 0 0 50px rgba(255, 255, 255, .2),
 -15px 0 0 50px rgba(255, 255, 255, .15),
 -40px 0 0 50px rgba(255, 255, 255, .21),
 10px 40px 0 10px transparent,
 70px 35px 0 10px transparent,
 95px 40px 0 10px transparent,
 135px 20px 0 10px transparent,
 155px 15px 0 10px transparent,
 190px -20px 0 10px transparent;
 }
}` 


javascript

复制代码

`const btn = document.querySelector('#g-btn');
btn.addEventListener('click', (e) => {
 btn.setAttribute('class', btn.getAttribute("class") === "active" ? "" : "active");
});` 

来看看最终效果:

是不是基本上还原了原效果?这里我们仅仅使用了一个标签,核心配合了 box-shadow 以及背景渐变完成了整个按钮效果。

完整的代码,你可以戳这里 CodePen Demo -- Single Div BTN Toggle Effect

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
css2026-06-17
外阴影和内阴影介绍
在前端页面设计中,阴影是非常常见的视觉效果。它可以让元素看起来更有层次感,也可以用来表达悬浮、按压、凹陷、卡片、按钮等状态。 CSS 中最常用的阴影属性是 。它既可以实现外阴影,也可以实现内阴影。 一、什么是外阴影 外阴影指的是元素外部的阴影效果。 它会出现在元素边框之外,让元素看起来像是从页面中“浮起来”了一样。常见的卡片、弹窗、按钮悬浮效果,基本都会用到外阴影。 例如: 这个阴影表示:元素下方有一层柔和的黑色阴影,让卡片看起来更有立...
学习
css2026-05-15
border-image-slice详细介绍
是 CSS 系列属性里最关键、也最容易让人懵的一个属性。 它的作用可以简单理解为: 把一张图片切成九宫格,然后决定哪几块用来绘制元素的边框。 它通常和这些属性一起使用: 或者简写成: 其中 决定“怎么切图”。 一、 是什么? 普通的 只能画纯色边框: 但有时候我们想用图片作为边框,比如: 游戏按钮边框 像素风 UI 复古窗口 聊天气泡 卡牌边框 花纹装饰框 九宫格按钮背景 这时候就可以用 。 例如一张按钮图片长这样: 我们希望按钮变宽时...
学习
css2026-03-05
如何在最小副作用下创建 BFC
在 CSS 布局的江湖中, BFC (Block Formatting Context,块级格式化上下文) 是一个老生常谈却又至关重要的概念。无论是解决 高度塌陷 、 清除浮动 ,还是防止 垂直外边距重叠 ,BFC 都是我们的得力助手。 但是,创建 BFC 的方法有很多: 、 、 、 …… 哪种方法对现有布局的“破坏”最小? ❌ 过去的老方法 在过去,为了触发 BFC,我们不得不使用一些带有副作用的“黑客”技巧: : 元素会脱离文档流,...
学习
css2026-03-03
CSS过渡性能解析:transition: all
CSS过渡性能深度解析:为什么 可能是你页面的隐形杀手 一、引言:一个被忽视的性能陷阱 你是否曾在项目中这样写: 看似简洁优雅,却可能在低端设备上引发卡顿、掉帧。 真相 : 是前端性能优化中高频出现的“隐形陷阱”。本文将彻底揭开它的面纱。 二、核心概念: 到底在控制什么? 指定 哪些CSS属性变化时触发动画过渡 。它是过渡效果的“守门员”: | 值 | 含义 | 生成的CSS | | | | | | | 所有 可过渡属性变化时均触发动画...
学习
css2026-01-26
在CSS中,四种高度视口单位的区别
在CSS中,视口单位是构建响应式网页设计的重要工具,特别是针对移动端开发。除了传统的 ,CSS新引入了 、 和 等更精确的单位,解决传统单位在移动端的局限性。 四种高度视口单位的区别 | 单位 | 全称 | 含义 | 特点 | | | | | | | vh | Viewport Height | 传统视口高度单位,1vh = 视口高度的1% | 在移动端有问题:不考虑浏览器UI变化,经常导致内容被遮挡或出现意外滚动 | | svh |...
学习
css2025-11-04
keyframes 关键帧机制
一、什么是 @keyframes? 用于定义动画在不同时间点(关键帧)的样式变化;配合 属性应用到元素上。 定义动画 :使用 声明关键帧序列; 应用动画 :在选择器中通过 / 相关长属性控制表现。 二、基本语法 等价写法( / ): 三、与 属性配合使用 /相关长属性 | 属性名 | 含义 | 示例值 | | : | : | : | | | 动画名称 | | | | 持续时长 | 、 | | | 速度曲线 | / / / | | | 延...
学习

评论

请登录后发表评论

去登录
加载评论中...

目录