这个夜间模式切换开关成果是不是很炫酷,在短视频曾刷到过是一个国外的设计师看似是为了难为咱们前端开发设计了一个元素超多且动画简单的开关切换成果。

后果在逛 codepen 的时候发现真的被一个大佬给做进去了,成果真的很不错,而且还在原来的根底上减少了额定的元素,本文将逐渐解析大佬的实现过程。

动画剖析

基于开关的动画成果能够将相干动画步骤进行模块拆分,大略能够拆分为下列模块:

  • 开关外部的主色调由蓝色背景切换为彩色
  • 开关外部的圆由黄色(太阳的成果)切换为灰色(月亮的成果)
  • 开关外部的圆可见的有三个不同透明度的大圆追随切换动画
  • 开关为亮色时有两层云朵,并伴有小飞机飞过的动画(封面动图不残缺,能够看最初实现的成果)
  • 开关未暗色时有小星星且有闪动的成果,并伴有太空熊飞过的动画(封面动图不残缺,能够看最初实现的成果)
  • 开关切换为暗色时整个页面背景切换为暗色

除了上述比拟外围的局部内容,还有一些CSS相干的细节,比方开关的3D边框成果,开关外部的圆暗影成果,切换过程中的贝塞尔曲线成果等,本文次要拆解外围实现的代码,全副代码有趣味的能够看源码。

前置知识点

在正式开始前先介绍几个本文须要用到的知识点,不便后续的了解,首先是元素中以 aria- 结尾的属性。

aria- 结尾的属性是为了实现无障碍拜访(Accessibility)而制订的一组Web规范属性,通常利用于HTML元素上,以进步网站的可拜访性。其中“ARIA”全称为“Accessible Rich Internet Applications”,即可拜访的丰盛Internet应用程序。

本文会用到aria-pressedARIA属性,用于批示一个按钮或切换的状态是否被按下或关上。它能够是以下三种状态之一:

  • aria-pressed="false":按钮或切换处于敞开状态。
  • aria-pressed="true":按钮或切换处于开启状态。
  • aria-pressed="mixed":按钮或切换处于混合状态,例如在多个选项中抉择了一些但不是全副。

应用aria-pressed属性有助于进步网站的可拜访性,让应用屏幕阅读器的人员可能更好地了解页面上各个元素的作用和状态。

第二个是CSS函数 var(),用于援用先定义的变量值。它的语法为 var(--variable-name, default-value),其中:

  • --variable-name 示意变量名称,必须以两个连字符(--)结尾,其后能够是任何自定义名称。
  • default-value 是可选的,示意在指定变量未定义或有效时要应用的默认值。

例如,假如咱们定义了一个名为 --dark 的变量,当其值为1的时候,最终的代码则是 scale: 1,如果 --dark 没有定义则最终的代码是 scale: 0

.element {  scale: var(--dark, 0);}

应用变量能够帮忙咱们轻松地更新和保护样式表的数据,因为能够只更改变量的值,而不用在样式表每个呈现的中央都进行更改。

基于下面提到的 aria-pressed,通过JS点击事件动态控制元素的 aria-pressed 值。

BUTTON.setAttribute("aria-pressed", IS_PRESSED ? false : true);

理论也就是管制了 --dark 的值,间接的也管制了所有应用了 --dark 定义的色彩、大小、字体和其余款式属性的值。

[aria-pressed=true] {  --dark: 1;}

本文的CSS代码中将应用大量的 var(--dark, default-value) 管制元素的显示暗藏及动画成果。

代码实现

背景色彩的切换

接下来开始基于下面的动画剖析开始拆解各外围局部的具体代码实现,首先实现开关外部背景色彩的切换,外围代码如下,基于 --dark 的变动批改 background 背景色,最终计算出的值即是 --sky--night 的值,这里并没有应用到,只是为了不便查看,须要留神一点的是这里应用了 hsl 格局的色值,前面两个参数的值是须要百分比格局的值,所以减少了 * 1%

--sky: hsl(204, 53%, 47%);--night: hsl(229, 25%, 16%);background: hsl(  calc(204 + (var(--dark, 0) * 25))  calc((53 - (var(--dark, 0) * 28)) * 1%)  calc((47 - (var(--dark, 0) * 31)) * 1%));

整个页面的背景切换是在按钮点击的时候给body设置了 data-dark-mode 属性。

document.body.setAttribute("data-dark-mode", IS_PRESSED ? false : true);

通过 data-dark-mode 属性值取不同的背景色值。

:root {  --bg: hsl(219, 30%, 88%);  --color: hsl(219 30% 20%);}[data-dark-mode=true] {  --bg: hsl(219, 30%, 12%);  --color: hsl(219 30% 98%);}

开关外部圆的背景色切换动画

而后是开关外部的圆由黄色(太阳的成果)切换为灰色(月亮的成果),仔细观察切换过程中的成果能够发现,切换后的灰色圆是从左往右笼罩黄色圆的成果,由此可见实现形式能够是基于定位,灰色圆层级高于黄色圆,或者灰色圆是黄色圆的子元素,这里的实现是基于父子元素实现。

整个切换过程有两个动画,首先是整个圆从左往右的平移过程,默认 --dark 没有定义时最终计算出的值是 0px--dark 为1时基于以后的总宽度减去的本身圆的宽度「3 / 8 * var(--width)」即是须要平移的间隔。

translate: calc(    var(--dark, 0) * (var(--width) - (3 / 8 * var(--width)))  ) 0;

而后圆外部的背景切换动画,默认月亮背景元素进行平移X轴100%,切换实现后批改平移X轴为0%。

translate: calc((100 - (var(--dark, 0) * 100)) * 1%) 0%;

再减少相应的过渡动画就能够看到两个背景色可见的切换过程成果。

transition: translate var(--speed) ease-in-out;

内部的父元素减少了 overflow: hidden,所以默认平移X轴100%是不可见的,大家看上面没有减少 overflow: hidden 代码的成果就更清晰实现的原理了。

  • 减少三个不同透明度的圆追随切换动画

这个实现绝对比较简单,基于 radial-gradient 径向突变绘制三层背景色,其实就是同一个色值突变的开始和完结地位不一样叠在一起造成的不同深浅色彩。基于定位将这个背景定位于圆的核心,平移的动画和下面圆的平移相似,只是因为背景区域的扩充,导致平移的数值会变小,这里就不贴代码了。

background:    radial-gradient(hsl(0 0% 100% / 0.25) 40%, transparent 40.5%),    radial-gradient(hsl(0 0% 100% / 0.25) 56%, transparent 56.5%)    hsl(0 0% 100% / 0.25);

云朵 & 星星切换动画

云朵 & 星星的复杂度就高很多了,所以这里不再应用纯CSS实现,基于svg实现,svg波及的代码量很多就不全副贴出来了,云朵总共有两个色彩,所以是有两块svg代码,能够看到上面的path对应的fill色值是不同的,其余的就是具体的path门路不一样了。

<!-- light模式的云1 --><svg aria-hidden=true class="toggle__backdrop" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 290 228">  <g class="clouds">    <path fill="#D9D9D9" d="M335..." />  </g></svg><!-- light模式的云2 --><svg aria-hidden=true class="toggle__backdrop" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 290 228">  <g class="clouds">    <path fill="#fff" d="M328..." />  </g></svg>

这里的云朵和星星的动画略微有点不一样,不再是左右平移,而是高低平移,所以 translate 只批改Y轴的值。

transition: translate var(--speed) var(--easing);translate: 0 calc(  var(--dark, 0) * (100% - (3 / 8 * var(--width))));

认真看有的星星会有闪动的成果,其实就是通过给对应svg元素设置动画管制元素的缩放,并给不同的星星设置不同的执行工夫和提早执行工夫。

animation: twinkle 4s -2s infinite;@keyframes twinkle {  0%, 40%, 60%, 100% {    transform: scale(1);  }  50% {    transform: scale(0);  }}

太空熊 & 小飞机

这两个元素都有两个动画,比方太空熊在飞过开关区域的时候本身是在旋转的,就像在太空一样,小飞机飞过的时候也不是直线飞出的,在动画过程中Y轴减少了肯定比例的变动。这里有些不一样的时候下面的平移动画都是左右高低挪动,能够看到这里是斜着平移的,阐明在平移的时候两个方向的值都在发生变化,以下是太空熊相干动画的代码。小飞机相干的代码就不再列举了,外围都是通过平移配合过渡实现动画成果。

// 平移动画translate: calc(var(--dark, 0) * 400%) calc(var(--dark, 0) * -350%);transition: translate calc(var(--speed) + (var(--dark, 0) * (var(--bear-speed) - var(--speed)))) calc(var(--bear-speed) * (0.4 * var(--dark, 0))) linear;// 本身旋转动画rotate: calc(var(--dark, 0) * 360deg);transition: rotate calc(var(--speed) + (var(--dark, 0) * (var(--bear-speed) - var(--speed)))) calc(var(--bear-speed) * 0.4) linear, scale var(--speed) ease-in-out;

最初

在这里咱们曾经介绍了整个实现过程的外围,实现了交互成果,但还有一些其余的性能如3D暗影、过渡、贝塞尔曲线动画等没有进行具体的介绍。如果对这些性能感兴趣,能够查看源代码进行具体理解。

初看起来,交互成果可能会让人感到十分辣手,各种细节成果都混在一起。然而,一旦开始了实现,就会发现其实并不难。将工作拆分成一个个小步骤去实现,大事化小小事化了。

看完本文如果感觉有用,记得点个赞反对,珍藏起来说不定哪天就用上啦~

专一前端开发,分享前端相干技术干货,公众号:南城大前端(ID: nanchengfe)

参考

源码:https://codepen.io/jh3y/pen/LYgj

在线预览:https://code.juejin.cn/pen/7231072547259809852