详解在 vaw-layouts 中通过 sass 实现动态换肤功能 (一)

2022-10-25 21:31:09 浏览数 (2)

在以往的前端开发中,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,如下:

代码语言:javascript复制
.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;
  	}
  ...
  }

详解在 vaw-layouts 中通过 sass 实现动态换肤功能 (一)

原创2021-06-03 22:22·知码

在以往的前端开发中,css 一直不能称之为一种编程语言,虽然在 css3中增加了几个变量和函数,但是实现的功能还是比较有限。后来出现了几个css预处理器,比较常用的有:sass、less 、stylus。有了他们,我们可以对css进行一些简单的编程。如果您对他们还不了解,可以查看一下他们的官网学习一下,vaw-layouts项目中使用的的预处理器是sass,因此本文也是基于sass进行讲解实现动态换肤的功能。

先说一下实现换肤的大体思路:

  • 提前定义好几个不同命名空间下的class
  • 通过js实现对元素动态切换class属性值

这种方式有几个缺点:

  • 要提前定义好不同命名空间下的class,不够灵活
  • 会增加打包后的应用体积

但是好在这种方式比较简单,适合大多数的场景。因此我们也采用了这种方式。

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,如下:

代码语言:javascript复制
.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

代码语言:javascript复制
.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代码如下:

代码语言:javascript复制
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元素上

代码语言:javascript复制
<body class="theme_color_blue light">
  ....
</body>

这样就可以完了动态换肤,你以为这就完了?肯定不是,这样有一个问题:

1、如果我们以后再定义几个主题,还得再写一遍上面的代码吗,大量冗余的代码,肯定不好

这个时候就体现出sass的强大了,我们知道,换肤就是改一下那几个变量的值:mainContentBgColor,mainTextColor。如果我们可以通过js动态地修改这几个值,不也可以实现动态换肤?答案是不可以,因为预处理器是在我们开发阶段使用,一旦打包之后,他们就没有了,我们也就没办法再改这些值。所以我们还是老老实实写恶心的代码?当然不行,虽然不可以动态改变这几些,但我们可以提前定义好不同命名下的变量,再通过scss生成不同的全名空间不就行了。如下代码:

代码语言:javascript复制
// 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

代码语言:javascript复制
@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:

代码语言:javascript复制
@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;
      }
    }
  }
}

通过上面的代码我们就实现了:

代码语言:javascript复制
.dark{
  .xx1{}
  .xx2{}
  .xx3{}
}

这样的命名空间class,说了这么多就是为了生成这种样式的class。有点麻烦啊,但这样有一个好处是:我们以后再生成不同的主题就能生成 变量值就好了,不用写一个个class了。是不是很爽~~ 好了,文章太长了,但功能还没有完全实现,下一篇讲解如何实现动态换主题色。敬请期待~ 最后看一下效果图:

0 人点赞