Published on

主题切换

Authors
  • avatar
    Name
    Reeswell
    Twitter

在网页开发中,为了使用户能够在不同的场景下有更好的浏览体验,我们常常需要针对不同的设备、不同的环境、不同的用户习惯等制定不同的样式方案。而在这些样式方案中,主题切换功能也是一个常见的需求,它可以让用户根据自己的喜好或需要切换网站的主题颜色,从而获得更好的视觉效果和更好的使用体验。

在本文中,我们将使用 Sass(一种 CSS 预处理器)来实现一个简单的主题切换功能。我们将定义两套主题(浅色主题和深色主题),并在切换主题时通过切换 body 元素的类名来实现切换。我们还将使用 Sass 的 mixin 和函数来优化代码,使其更易于维护和拓展。

定义主题样式

我们首先需要定义两套主题的颜色值。为了方便管理和使用,我们将这些颜色值定义在一个 map 中,然后使用 Sass 的全局变量和 mixin 来在不同的主题下应用这些颜色值。以下是一个示例:

theme.scss
// 定义主题颜色值
$themes: (
  light: (
    textColor: #333,
    bg: #f9f9f9,

  ),
  dark: (
    textColor: #ddd,
    bg: #333,
  ),
);

// 定义 mixin,用于在指定主题下应用样式
@mixin useTheme($themes) {

  @each $theme,
  $map in $themes {
    // 定义全局变量,存储当前主题的颜色值
    $theme-map: () !global;

    // 循环遍历当前主题的颜色值 map
    @each $key,
    $submap in $map {
      // 获取当前主题下 $key 对应的颜色值
      $value: map-get(map-get($themes, $theme), "#{$key}");
      // 将当前主题的颜色值合并到 $theme-map 变量中
      $theme-map: map-merge($theme-map,
        ($key: $value,
        )) !global;
    }

    // 在当前主题下应用样式
    .theme-#{$theme} & {
      @content; // 插槽的作用
    }

    // 清空 $theme-map 变量,为下一次循环做准备
    $theme-map: null !global;
  }
}

// 定义函数,用于获取指定键名的颜色值
@function themed($key) {
  @return map-get($theme-map, $key);
}

// 定义 mixin,用于在移动设备下应用样式
@mixin mobile {
  @media (max-width: 480px) {
    @content;
  }
}

// 定义 mixin,用于在平板设备下应用样式
@mixin tablet {
  @media (max-width: 960px) {
    @content;
  }
}

在上述代码中,我们定义了一个名为 $themesmap,其中包含两个子 map,分别代表浅色主题和深色主题。每个子 map 包含两个键值对,textColor 和 bg 分别代表文本颜色和背景颜色。 我们还定义了一个 useTheme mixin,用于在指定主题下应用样式。这个 mixin 接受一个参数 $themes, 并使用 @each 循环遍历每个主题。在循环体内,我们定义了一个全局变量 $theme-map,用于存储当前主题的颜色值。 然后,我们使用@each再次循环遍历当前主题的颜色值 map,获取相应的颜色值,并将其合并到 $theme-map 变量中。 最后,我们使用 .theme-#{$theme}选择器为当前主题下的所有元素应用样式,并使用 @content 插槽来插入实际的样式代码。 在样式代码中,我们使用了 themed() 函数来获取当前主题下的颜色值。

组件中使用主题样式

在组件 CSS 中,我们可以在组件中为不同的主题定义不同的样式。以下是一个示例:

style.scss
// 应用样式到组件下
.container {
  @include useTheme($themes) {
    color: themed('textColor');
    background-color: themed('bg');
    // 文本样式
    .text {
      background-color: themed('bg');
      color: themed('textColor');
      border: 1px solid themed('textColor');
      padding: 10px;
      border-radius: 5px;
    }
        // 使用 mobile mixin 定义移动设备下的样式
    @include mobile {
      .button {
        font-size: 14px;
      }

      .text {
        font-size: 16px;
      }
    }

    // 使用 tablet mixin 定义平板设备下的样式
    @include tablet {
      .button {
        font-size: 16px;
      }

      .text {
        font-size: 18px;
      }
    }
  }
}

在上述代码中,我们使用 useTheme mixin 来应用样式。在应用样式时,我们使用 themed() 函数来获取当前主题下的颜色值。 上面style.scss内容将会编译成以下css

style.css
.theme-light .container .text {
  background-color: #f9f9f9;
  color: #333;
  border: 1px solid #333;
  padding: 10px;
  border-radius: 5px;
}
@media (max-width: 480px) {
  .theme-light .container .button {
    font-size: 14px;
  }
  .theme-light .container .text {
    font-size: 16px;
  }
}
@media (max-width: 960px) {
  .theme-light .container .button {
    font-size: 16px;
  }
  .theme-light .container .text {
    font-size: 18px;
  }
}
.theme-dark .container .text {
  background-color: #333;
  color: #ddd;
  border: 1px solid #ddd;
  padding: 10px;
  border-radius: 5px;
}
@media (max-width: 480px) {
  .theme-dark .container .button {
    font-size: 14px;
  }
  .theme-dark .container .text {
    font-size: 16px;
  }
}
@media (max-width: 960px) {
  .theme-dark .container .button {
    font-size: 16px;
  }
  .theme-dark .container .text {
    font-size: 18px;
  }
}/*# sourceMappingURL=style.css.map */

实现主题切换功能

我们现在已经定义了两套主题的颜色值,并且可以使用 useTheme mixin 和 themed() 函数来在不同的主题下应用这些颜色值。接下来,我们可以实现主题切换功能。具体实现方式是,当用户点击切换主题按钮时,我们通过 JavaScript 代码来切换 body 元素的类名,从而切换到另一套主题的样式。

以下是一个示例 JavaScript 代码:

// 获取切换主题按钮元素
const themeSwitch = document.querySelector('#theme-switch');

// 监听按钮的点击事件
themeSwitch.addEventListener('click', () => {
  // 切换 body 元素的主题类名
  document.body.classList.toggle('theme-light');
  document.body.classList.toggle('theme-dark');
});

在上述代码中,我们首先获取切换主题按钮和 body 元素。然后,我们给切换主题按钮添加一个点击事件监听器。当用户点击按钮时,我们调用 classList.toggle() 方法来切换 body 元素的类名。如果 body 元素已经有 theme-dark 类,则该方法会删除该类;否则,它会添加该类。

总结

在本文中,我们使用 Sass 来实现了一个简单的主题切换功能。我们首先定义了两套主题的颜色值,并使用 useTheme mixin 和 themed() 函数来在不同的主题下应用这些颜色值。然后,我们通过 JavaScript 代码来切换 body 元素的类名,从而切换到另一套主题的样式。最后,我们在 CSS 中为不同的主题定义不同的样式。这个示例可以作为一个基础模板,您可以根据自己的需求进行修改和拓展。

本文案例代码