在以往的前端开发中,css 一直不能称之为一种编程语言,虽然在 css3中增加了几个变量和函数,但是实现的功能还是比较有限。后来出现了几个css预处理器,比较常用的有:sass、less 、stylus。有了他们,我们可以对css进行一些简单的编程。如果您对他们还不了解,可以查看一下他们的官网学习一下,vaw-layouts项目中使用的的预处理器是sass,因此本文也是基于sass进行讲解实现动态换肤的功能。
先说一下实现换肤的大体思路:
1、提前定义好几个不同命名空间下的class
2、通过js实现对元素动态切换class属性值
这种方式有几个缺点:
1、要提前定义好不同命名空间下的class,不够灵活
2、会增加打包后的应用体积
但是好在这种方式比较简单,适合大多数的场景。因此我们也采用了这种方式。
vaw-layouts中有四种不同的主题:light、dark、dark-side、blue-side
实际上主要的有两种:light dark。因为dark-side、blue-side两种主题只是对 sidebar和navbar进行了适配,主体内容并没有适配,因此我们主要说一下light和dark
其实如果我们不考虑dark的情况下,默认的就是light,我们可以把一个div设置成白色的背景,黑色的字体。这就是light主题了。所以light主题并不用多说什么,按照我们平常的开发该怎么写就怎么写。我们重点说一下怎么适配dark主题,正如前面说的,要实现动态换肤就可定义好不同的命名空间下的class,如下:
.dark{ .el-button { background: $mainContentBgColor; color: $mainTextColor; } .el-table { background-color: $mainCardBgColor; color: $mainTextColor; .hover-row { background-color: $mainCardBgColor !important; } th, tr { background-color: $mainCardBgColor !important; } th.is-leaf, td { border-bottom: 1px solid $mainTextColor; } } div{ background: $mainContentBgColor; color: $mainTextColor; } ... }
这里只是简单说明一下,还有大量的css代码未列出。
说明一下:以 $开头的都是scss下定义的变量,如果不明白,还请查看官方文档。
1、$mainContentBgColor 主要指定背景色
2、$mainTextColor 主要指定文本颜色
同理,我们也定义好light下的class
.light{ .el-button { background: $mainContentBgColor; color: $mainTextColor; } .el-table { background-color: $mainCardBgColor; color: $mainTextColor; .hover-row { background-color: $mainCardBgColor !important; } th, tr { background-color: $mainCardBgColor !important; } th.is-leaf, td { border-bottom: 1px solid $mainTextColor; } } div{ background: $mainContentBgColor; color: $mainTextColor; } ... }
然后当我们需要切换背景色的时候就可以改变一下元素的class属性就好了,js代码如下:
export function toggleThemeClass(element, className) { if (!element || !className) { return } let classString = element.className const clazz = classString.split(' ').filter(it => it.indexOf('theme_') !== -1) clazz.push(className) classString = clazz.join(' ') element.className = classString }
我们主要是把class应用到了body元素上
<body class="theme_color_blue light"> .... </body>
这样就可以完了动态换肤,你以为这就完了?肯定不是,这样有一个问题:
1、如果我们以后再定义几个主题,还得再写一遍上面的代码吗,大量冗余的代码,肯定不好
这个时候就体现出sass的强大了,我们知道,换肤就是改一下那几个变量的值:$mainContentBgColor,$mainTextColor。如果我们可以通过js动态地修改这几个值,不也可以实现动态换肤?答案是不可以,因为预处理器是在我们开发阶段使用,一旦打包之后,他们就没有了,我们也就没办法再改这些值。所以我们还是老老实实写恶心的代码?当然不行,虽然不可以动态改变这几些,但我们可以提前定义好不同命名下的变量,再通过scss生成不同的全名空间不就行了。如下代码:
// dark下的变量 @function getDarkMaps($primaryColor) { @return ( dark-menuBgColor: #141414, dark-contentBgColor: #0f0f10, dark-activeTextColor: $primaryColor, dark-textColor: #f5f5f5, dark-menuActiveBgColor: #2f2f2f, dark-badgeColor: #000000, dark-headerBgColor: #141414, dark-headerBorderColor: $primaryColor, dark-headerTextColor: #ffffff, dark-headerShadow: #7b7b7b, dark-headerTabItemBgColor: #2f2f2f, dark-headerTabItemActiveBgColor: #2f2f2f, dark-headerTabItemTextColor: #f5f5f5, dark-headerTabItemActiveTextColor: $primaryColor, dark-mainBorderColor: $primaryColor, dark-contentActiveBgColor: #2f2f2f, dark-mainContentBgColor: #000000, dark-mainCardBgColor: #141414, dark-mainCardBorderColor: $primaryColor, dark-mainTextColor: #f5f5f5, dark-mainActiveTextColor: $primaryColor, dark-popoverBgColor: #000, dark-popoverBorderColor: $primaryColor, dark-popoverTextColor: #f5f5f5 ); } // light下的变量 @function getLightMaps($primaryColor) { @return ( light-menuBgColor: #ffffff, light-contentBgColor: #f7faff, light-activeTextColor: #ffffff, light-textColor: #333333, light-menuActiveBgColor: $primaryColor, light-badgeColor: $primaryColor, light-headerBgColor: #ffffff, light-headerTextColor: #333333, light-headerBorderColor: #f5f5f5, light-headerShadow: #f5f5f5f5, light-headerTabItemBgColor: #e8f4ff, light-headerTabItemActiveBgColor: #e8f4ff, light-headerTabItemTextColor: rgb(97, 97, 97), light-headerTabItemActiveTextColor: $primaryColor, light-mainBorderColor: #ffffff, light-contentActiveBgColor: $primaryColor, light-mainContentBgColor: #f0f2f5, light-mainCardBgColor: #ffffff, light-mainCardBorderColor: #f5f5f5, light-mainTextColor: #333333, light-mainActiveTextColor: #888888, light-popoverBgColor: #ffffff, light-popoverBorderColor: #f5f5f5, light-popoverTextColor: #333333 ); }
虽然这样也有冗余的代码,但比起一个个定义class 这样还算可以的
再通过 @each指令生成class
@mixin theme($themeName: 'light') { @each $key, $value in $themeColorsMap { // 这里就是生成类似于:.dark.theme_color_blue // 因为我们还有切换主题色的功能,所以还得适配不同的颜色 .#{$themeName}.#{$key} { @include mainLayout( $mainContentBgColor: map-get($map: $value, $key: #{$themeName}-mainContentBgColor), $mainCardBgColor: map-get($map: $value, $key: #{$themeName}-mainCardBgColor), $mainCardBorderColor: map-get($map: $value, $key: #{$themeName}-mainBorderColor), $mainTextColor: map-get($map: $value, $key: #{$themeName}-mainTextColor), $mainActiveTextColor: map-get($map: $value, $key: #{$themeName}-mainActiveTextColor), $parent: $themeName, ); } } }
再看一下:mainLayout:
@mixin mainLayout( $mainContentBgColor: $mainContentBgColor, $mainCardBgColor: $mainCardBgColor, $mainCardBorderColor: $mainCardBorderColor, $mainTextColor: $mainTextColor, $mainActiveTextColor: $mainActiveTextColor, $parent: "light" ) { .vaw-main-layout-container { background-color: $mainContentBgColor; color: $mainTextColor; } .footer-container { background: $mainCardBgColor; border-top: 1px dashed #848484; color: $mainTextColor; } @if $parent == dark { .el-button { background: $mainContentBgColor; color: $mainTextColor; } .el-table { background-color: $mainCardBgColor; color: $mainTextColor; .hover-row { background-color: $mainCardBgColor !important; } th, tr { background-color: $mainCardBgColor !important; } th.is-leaf, td { border-bottom: 1px solid $mainTextColor; } } .el-table::before { display: block; } .el-table__body tr:hover > td { background-color: $mainContentBgColor !important; } .el-table__body tr.current-row > td { background-color: $mainContentBgColor !important; } .el-table__body tr.hover-row > td { background-color: $mainContentBgColor !important; } .el-message-box { background-color: $mainCardBgColor; .el-message-box__title { color: $mainActiveTextColor; } .el-message-box__content { color: $mainTextColor; } } } }
通过上面的代码我们就实现了:
.dark{ .xx1{} .xx2{} .xx3{} }
这样的命名空间class,说了这么多就是为了生成这种样式的class。有点麻烦啊,但这样有一个好处是:我们以后再生成不同的主题就能生成 变量值就好了,不用写一个个class了。是不是很爽~~
好了,文章太长了,但功能还没有完全实现,下一篇讲解如何实现动态换主题色。敬请期待~
最后看一下效果图: