前端 “一键换肤“ 的几种方案

更新日期: 2022-04-07阅读: 1.1k标签: 主题

现在越来越多的网站都提供了拥有换肤(切换主题)功能,如 ElementUI,既是为了迎合用户需求,或是为了凸显自己特点,因此提供了个性化定制的功能.

其实之前就想了解和实现 “一键换肤” 功能,但是由于种种原因一直拖到了现在.


css 样式覆盖实现

核心

通过切换 css 选择器的方式实现主题样式的切换.

在组件中保留不变的样式,将需要变化的样式进行抽离

提供多种样式,给不同的主题定义一个对应的 CSS 选择器

根据不同主题设置不同的样式

实现

下面通过 vuex 存储和控制全局的主题色,其代码如下:

import { createStore } from 'vuex'

// 创建一个新的 store 实例
const store = createStore({
  state () {
    return {
      theme: 'light'
    }
  },
  mutations: {
    setTheme (state, payload) {
      state.theme = payload
      document.querySelector('body').className = payload
    }
  }
})

export default store

在 template 模板中通过 vuex 中的主题设置对应类名,如头部代码如下:

<template>
  <div :class="['header', store.state.theme]">
    <span>{{title}}</span>
    <input v-model="checked" type="checkbox" class="switch" @change="changeTheme" />
  </div>
</template>

下面 theme.css 中通过 .light 和 .dark 两个类选择器来区分明亮主题和暗黑主题,并且事先准备了它们对应的样式,如下:

/* light 默认主题*/
body.light {
  background-color: #fff;
}

.header.light {
  background-color: #fff;
  border-bottom: 1px solid #d6d6d6;
  color: rgb(51, 50, 50);
}

.list.light .title {
  color: rgb(51, 50, 50);
}
.list.light .describe{
  color: rgb(158, 158, 158);
}

.list.light .left{
  border: 1px solid rgb(51, 50, 50);
}

/* dark 暗黑主题 */
body.dark {
  background-color: rgb(51, 50, 50);
}

.header.dark {
  background-color: rgb(51, 50, 50);
  border-bottom: 1px solid #fff;
  color: #fff;
}

.list.dark .title {
  color: #fff;
}
.list.dark .describe{
  color: rgb(201, 201, 201);
}
.list.dark .left{
  border: 1px solid #fff;
  background-color: #fff;
}

缺点

多种主题样式都要引入,导致代码量增大

样式不易管理

查找样式复杂

开发效率低

拓展性差

...

实现多套 CSS 主题样式

核心

实现多套 CSS 主题样式,根据用户切换操作,通过 link 标签动态加载不同的主题样式,主要解决了多个主题色被编译到一个文件中导致单个文件过大.

实现

css 部分直接拆分成 ligth.css 和 dark.css 两个文件,设置主题部分的 setTheme.js 代码如下:

export default function setTheme(theme = 'ligth') {
  let link = document.querySelector('#theme-link')
  let href = "/theme/" + theme + ".css"
  
  if (!link) {
    let head = document.querySelector('head')
    link = document.createElement('link')
    link.id = '#theme-link'
    link.rel = "stylesheet"
    link.href = href
    head.appendChild(link)
  } else {
    link.href = href
  }
}

缺点

需要重复 CV 多份样式文件进行单独修改

没有单独提取出可变的样式部分

需要提前知道打包后的文件路径,否则可能导致主题样式引入错误

...

CSS 变量实现

核心

通过 body.style.setProperty(key, value) 动态修改 body 上的 CSS 变量,使得页面上的其他部分可以应用最新的 CSS 变量对应的样式.

实现

theme.css 中负责定义全局的 CSS 变量,代码如下:

/* 实现方式一 */
:root {
  --theme-bg: initial; // 背景色
  --theme-color: initial; // 字体色
  --theme-boder-color: initial; // 边框色
}

====================================================

/* 实现方式二 */
/* 默认值:light */
:root {
  --theme-bg: #fff;
  --theme-color: rgb(51, 50, 50);
  --theme-img-bg: #fff;
  --theme-boder-color: #d6d6d6;
}

/* 暗黑:dark */
[>'dark'] {
  --theme-bg: rgb(51, 50, 50);
  --theme-color: #fff;
  --theme-boder-color: #fff;
}

themeUtil.js 中负责获取当前对应样式值,以及设置 body 上的 CSS 变量值,如下:

const darkTheme = 'rgb(51, 50, 50)'
const lightTheme = '#fff'
const lightBorderTheme = '#d6d6d6'

// 获取对应的主题色值
export const getThemeMap = (isLight) => {
  return {
    'theme-bg': isLight ? lightTheme : darkTheme,
    'theme-color': isLight ? darkTheme : lightTheme,
    'theme-boder-color': isLight ? lightBorderTheme : lightTheme,
  }
}

// 设置主题色值
export const setTheme = (isLight = true) => {
  const themeMap = getThemeMap(isLight)
  const body = document.body
  /* 实现方式一 */
  Object.keys(themeMap).forEach(key => {
    body.style.setProperty(`--${key}`, themeMap[key])
  })
  
  /* 实现方式二 */
  // body.style.setProperty('data-theme', isLight ? 'light' : 'dark')
}

通过 var() 在组件中应用对应 CSS 变量,比如在头部中的使用:

<style scoped>
.header {
  ...省略
  color: var(--theme-color);
  border-bottom: 1px solid var(--theme-boder-color);
  background-color: var(--theme-bg);
}
...省略
</style>

缺点

缺点就是兼容性不好


兼容

通过 css-vars-ponyfill 对 CSS 变量进行兼容处理,themeUtil.js 中代码改变如下:

import cssVars from "css-vars-ponyfill";

const darkTheme = 'rgb(51, 50, 50)'
const lightTheme = '#fff'
const lightBorderTheme = '#d6d6d6'

// 这里定义的 键/值 对,是为了给 cssVars 传参
export const getThemeMap = (isLight) => {
  return {
    '--theme-bg': isLight ? lightTheme : darkTheme,
    '--theme-img-bg': lightTheme,
    '--theme-color': isLight ? darkTheme : lightTheme,
    '--theme-boder-color': isLight ? lightBorderTheme : lightTheme,
  }
}

export const setTheme = (isLight = true) => {
  const themeMap = getThemeMap(isLight)
  const body = document.body
  
  /* 实现方式一 */
  Object.keys(themeMap).forEach(key => {
    body.style.setProperty(key, themeMap[key])
  })
  
  /* 实现方式二 */
  // body.style.setProperty('data-theme', isLight ? 'light' : 'dark')
  
  // 实现兼容方案
  cssVars({
    watch: true, // 添加、删除、修改 <link> 或 <style> 元素的禁用或 href 属性时,ponyfill 将自行调用    
    variables: themeMap, // variables 自定义属性名/值对的集合
    onlyLegacy: false, // false  默认将 css 变量编译为浏览器识别的 css 样式 ;true 当浏览器不支持css变量的时候将css变量编译为识别的css  
  });
}

主题图片切换

实现了前面的内容之后,现在给分别给 light 和 dark 主题添加一个 logo,这一部分其实很简单了,下面的示例代码是基于 Vue3 进行实现的

// Header.vue
<script setup>
import { ref } from 'vue'
import { setTheme } from '../style/themeUtil'

defineProps({
  title: String
})

const checked = ref(false)

const logoUrl = ref('')

const loadImg = async () => {
  let name = !checked.value ? 'light' : 'dark'
  let ext = !checked.value ? 'png' : 'jpg'
  let res = await import(`../assets/logo-${name}.${ext}`)
  logoUrl.value = res.default
}

loadImg()

const changeTheme = (event) => {
  setTheme(!checked.value)
  loadImg()
}

</script>

<template>
  <div class="header">
    <img class="logo" :src="logoUrl" />
    <span>{{ title }}</span>
    <input v-model="checked" type="checkbox" class="switch" @change="changeTheme" />
  </div>
</template>
作者:熊的猫
链接:https://juejin.cn/post/7063010855167721486

链接: https://www.fly63.com/article/detial/11328

wordpress大前端DUX主题

DUX主题是基于WordPress程序的主题,由themebetter团队原创开发,是目前比较火的wordpress主题;支持百度熊掌号,适用于垂直站点、科技博客、个人站,扁平化设计、简洁白色、超多功能配置

Vue实现换主题/皮肤功能

网站换肤,主题切换。网站的主题色可以在几种常用颜色之间进行切换,还有相关图片、图标也要跟随主题进行切换。文章由两部分组成:css切换,图片图标切换

Web前端主题切换的几种方案

本文将介绍 Web 前端主题切换的几种常用方案,示例代码基于 React 框架。废话少说,show you the code!这种方案利用了css多层样式精确匹配的特点,通过样式覆盖的方式实现主题的切换。

采用CSS3的VAR实现动态主题切换

官方称呼其为自定义属性,坊间通常叫做css变量,因为它类似于其他js语言中的变量,可用于存储颜色、字体、大小宽度等css属性值;定义方式为两个连字符也就是中划线(--)开头,引用方式为var(custom-property-name, value)

Sass应用之实现主题切换

实现主题切换有几种不同的方案,比如使用CSS变量,使用JavaScript动态加载对应的主题样式文件等。本文主要讲的是如何使用Sass实现主题切换。

前端实现切换主题方案

前端开发人员面临着制作增强用户体验和适应用户偏好的用户界面的任务。带有 css 的 react 主要可用于创建多色可切换主题。为用户提供了在给定时间点在主题颜色之间切换以适合他们的偏好的特权。

使用CSS变量实现主题定制真的很简单

Varlet是通过css变量来组织样式的,什么是css变量呢,其实很简单,首先声明自定义的css属性,随便声明在哪个元素上都可以,不过只有该元素的后代才能使用,所以如果要声明全局所有元素都能使用的话,可以设置到根伪类:root下

常用Vue UI框架的主题切换

在如今,很多网页已经可以手动切换明亮模式和黑暗模式,网页的主题切换已经成为了一个常用的需求,因此,本文将从常见框架的处理方式总结一些相关的操作,并探究其本质。

兼容ie浏览器 网站变灰色

在遇到特殊情况的时候,我们作为站长需要紧急将网站变灰的需求,大家都知道通过简单的CSS样式即可实现,上述代码配合指定网页在IE内核下的渲染模式,就可以达到效果,这个语句意思便是告诉IE浏览器

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!