点击上方蓝字关注我们

导语
说起Dark mode概念,彻底开始流行还得从苹果公司所推出的IOS 13开始,随着移动设备在硬件创新上发展的瓶颈期到来之后,大家的目光更多地转向了软件层面的优化,从IOS13的dark mode,再到文件系统、小组件。苹果一步步把以前没挤的牙膏一点点地还了回来,而最令人啧啧称道的,并且影响范围最广的,不是近几年推出的根据色温或时区变化的true tone显示技术,而是更直接暴力的深色模式。自提出以来,移动端、PC端跟进迅速,不仅仅是IOS平台,MAC OS、Windows也开始跟进,各大厂软件纷纷适配。但对于B S架构来讲,web在深色模式的处理上就显得相形见绌,在众多的项目中对于css样式的编写通常都只有一份,如今却需要准备另一份截然不同的设计稿,工作量增大了足足一倍。Chrome也尝试推出了自动转换网页色差技术Force Dark Mode for Web Contents,但终究还是差强人意,本文将会谈谈云趣科技对于Web端Dark mode的解决方案。
01
技术架构
当然,在真正谈及深色模式解决方案之前,我们是需要一些前置条件的,前端技术栈多种多样,对于B端产品,大致可分为三款耳熟能详的框架,Vue、React、Angular。一份兼容深色浅色的设计稿可以不考虑底层技术,但所使用的组件必须需要支持变色模式,否则深色模式的兼容成本会太高,综上所述,在真正进入深色模式的开发之前,首先需要一套独立的组件库,例如vue阵营中的代表element-ui,iview,或react阵营中的antd,在对组件有一个深层次聚合整理后,才能从设计模式上推动统一的主题色更改方案。
谈到组件库,React最具代表性的是Ant Design,Vue则是Element UI以及IView,而他们各自都实现了切换主题色的功能,从这个角度出发,我们可以想到的是,既然主题色可以切换,那深色模式是不是范围更加广泛的主题色切换呢?一定程度上是可以这么看的,我们可以看看他们各自的实现方式都是什么。
Ant Design在3.x的版本中采用的是less modifyVars特性,在less打包后可以保留当时预设的色值变量,由浏览器再次插入新的颜色变量后直接编译生成一份全新的css文件,最后以link标签的方式插入回浏览器的dom树中,但这种方式可以看出浏览器端压力较大,所以在antd 3.x版本的官网中,切换主题色会造成明显的卡顿,使用体验不佳,就更何况范围更广的深色模式了。

切换有一定延迟,且根据客户端性能会产生一段时间中的卡顿
Element UI的实现方式比较讨巧,由于在scss中没有less对应的概念,所以无法通过预设的变量来直接编译更改css文件,他们是通过在css文件中注入了一些关键词,并在代码中维护了一份主题色对应的map,在真正主题切换时会直接通过维护的map去寻找对应的字符串并完成替换,最后再以link标签的方式重新插入回浏览器的dom树中。可以看到基本与antd的思路相同,只不过由于使用的css预处理组件不同而带来了第一步的差异,但最后load的方式是一样的。具体实现方式详见https://github.com/ElemeFE/element/issues/3054

ElementUI切换速度非常快,且新插入了一个style标签
总结完大厂的实现方式,再结合我们自己从网上搜寻的信息来看,大致可以分为五种做法来实现。
技术实现方案 | 实施难度 | 学习成本 | 运维成本 | 实现效果 |
less modifyVars | 较低 | 一般 | 较低 | 一般,性能欠佳 |
css关键词替换 | 一般 | 较低 | 较高 | 较高 |
多份css文件 | 较高 | 较低 | 较高 | 一般,取决于准备文件数量 |
css-in-js | 较高 | 较高 | 较低 | 较高 |
css variables | 较低 | 较低 | 较低 | 一般,完全不兼容IE |
02
技术选型
我们一个个来看方案。
首先方案一使用less特性,这就决定了你的组件库以及你的项目都需要选用less作为底层技术选型,否则就pass,这对于一个已经成型的项目来说,都是一个不小的工作量,并且切换编译时对于浏览器端的压力也是极大,并不是一个特别合适的方案。
方案二的方式非常讨巧,也是比较大众的实现方式,但由于scss没有提供相关特性,相当于编译替换变量环节完全由开发者承担,会带来较高的开发、维护成本,需要一定的权衡。
方案三的方式显得比较暴力,准备两份css文件从一定程度上看确实技术实现难度是最低的,但如果有需求改动,则需要copy两份代码,对于开发和维护显然是一件极其低效的行为,且由于没有任何变量介入,对于深色模式与主题色切换的排列组合实现也非常笨拙,需要提前准备。
方案四通过css-in-js技术来实现,其实使用react的jsx编程模式,那么一定程度而言,我们就已经是css in js写法了,当然,基于此特性我们也可以随意使用js来控制css的颜色变量,当然,对于传统开发css和js分离而言,这项新技术也会带来一定的心智负担和成本,并不是所有前端团队都可以接受。
方案五则是利用css最新的variables特性来完成,其特点是css可以有一个原生的载体存放变量,且可以被js所修改,这么一听确实比较符合我们的需求,但其缺点是完全不支持IE浏览器,在Can I Use中可以查到,除了IE外,对于其他浏览器的支持都较为友好。我们本次的方案也会围绕这个技术特性开始讲解。
03
CSS变量
该方法入门成本较低,且维护较为便利,只需要在html或body元素中定义一个相应的变量名,在其他类中直接调用该变量即可,并且js可以通过调用setProperty方法来更改相应色值。当然在使用该方法之前,我们需要通过can i use中确认一下各浏览器兼容性。

不幸的是,IE浏览器完全不支持,当然这里我们可以留存一份备选方法,当浏览器无法css语法时,将默认不使用该属性设置,并向上寻找可用继承类,所以我们可以通过设置一个颜色的默认值在var属性上方来规避该风险。

// csshtml {--yunqu-color: #000;}body {color: '#000';color: var(--yunqu-color);}// jsdocument.querySelector('html').style.setProperty('--yunqu-color','#fff')
云趣科技使用css variables特性切换,实现秒级别切换
04
总结
在技术上解决了dark mode的难题后,技术设计上也需要一份对称的color map以及UX方面的合理设计,这部分的设计推荐参考google 的 material design dark mode。
https://material.io/design/color/dark-theme.html#properties
综合来看,css variables特性的加入,使得主题更改方案的实现成本不会太高,且在性能方面也不会产生较大影响。
作者:钟灵
简介:云趣科技全栈工程师
出品:云趣科技


云趣 ,等你关注




