轻轻松松实现页面换肤

199 阅读3分钟

换肤功能(使用 css/less 动态更换主题色)

参考:

一、需求

一般来说换肤的需求分为两种:

  1. 一种是几种可供选择的颜色/主题样式,进行选择切换,这种要求可供选择的主题切换不会很多
  2. 另一种是需要自定义色值,或者通过取色板取色,可供选择的范围就很大了

二、实现

1. 对于可供选择的颜色/主题样式换肤的实现

- 一个全局 class 控制样式切换

切换的时候 js 控制样式的切换

- JS 改变 href 属性值切换样式表,例如:

<link id="skincolor" href="skin-default.css" rel="stylesheet" type="text/css" />
document.getElementById("#skincolor").href = "skin-red.css";

这种方式需要维护几个主题样式表,js 点击切换的时候通过改变 css 样式表链接来实现。 例如这个 demo

这种实现对于,颜色和主题多了的时候,维护起来就很麻烦,需要同时维护 n 个样式文件,并且使用 JS 改变 href 属性会带来加载延迟,样式切换不流畅,体验也不好。

但如果是有包含不同复杂背景图片切换的时候,这种方式可以实现,但其他更改方式,例如下面要说的 css 变量 less modifyVars 就无法实现了

- HTML 的 rel 属性下的 alternate 实现: MDN Alternative style sheets

示例:

<link href="reset.css" rel="stylesheet" type="text/css">
<link href="default.css" rel="stylesheet" type="text/css" title="Default Style">
<link href="fancy.css" rel="alternate stylesheet" type="text/css" title="Fancy">
<link href="basic.css" rel="alternate stylesheet" type="text/css" title="Basic">

所有样式表都可分为 3 类:

  • 没有 title 属性,rel 属性值仅仅是 stylesheet 的无论如何都会加载并渲染,如 reset.css;
  • 有 title 属性,rel 属性值仅仅是 stylesheet 的作为默认样式 CSS 文件加载并渲染,如 default.css;
  • 有 title 属性,rel 属性值同时包含 alternate stylesheet 的作为备选样式 CSS 文件加载,默认不渲染,如 red.css 和 green.css;

alternate 意味备用,相当于是 css 预加载进来备用,所以不会有上面那种切换延时

这个的使用: developer.mozilla.org/en-US/docs/…

link 的 disabled 属性

image.png

使用 JavaScript 代码修改<link>元素 DOM 对象的 disabled 值为 false,可以让默认不渲染的 CSS 开始渲染。实现 demo

2. 对于制定动态色值换肤的实现

  • 使用less的modifyVars
  • 使用CSS的变量

如果是要实现动态换肤,自定义色值,那上面的几种方式就不适合了。

已有的实现下面这些方式 Element-UI 有换肤功能 示例预览

实现原理: 官方解释

  1. 先把默认主题文件中涉及到颜色的 CSS 值替换成关键词: github.com/ElementUI/t…

  2. 根据用户选择的主题色生成一系列对应的颜色值:github.com/ElementUI/t…

  3. 把关键词再换回刚刚生成的相应的颜色值:github.com/ElementUI/t…

  4. 直接在页面上加 style 标签,把生成的样式填进去:github.com/ElementUI/t…

看这个实现,还是比较麻烦的,还有更优雅的方法来实现 Ant Design 的更换主题色功能是用 less 提供的 modifyVars 的方式进行覆盖变量来实现。

less 的 modifyVars 方法

modifyVars 方法是是基于 less 在浏览器中的编译来实现。所以在引入 less 文件的时候需要通过 link 方式引入,然后基于 less.js 中的方法来进行修改变量

less.modifyVars({
  "@themeColor": "blue",
});

link 方式引入主题色文件

<link rel="stylesheet/less" type="text/css" href="./src/less/public.less" />

更改主题色事件

// color 传入颜色值
handleColorChange (color) {
    less.modifyVars({  // 调用 `less.modifyVars` 方法来改变变量值'
         @themeColor':color
         })
    .then(() => {
         console.log('修改成功');
    });
};

如果发现项目运行报错如下:

.bezierEasingMixin();
^
Inline JavaScript is not enabled. Is it set in your options?

那可能是没有开启 javascriptEnabled:true

在 webpack 配置里开启

{
      test: /\.less$/,
      loader: 'less-loader',
      options: {
             javascriptEnabled: true
       }
},

less 方法仅限于用 less 的项目才能使用,查了下 sass 是没有类似 less.modifyVars 这种方法的。

上面不是通用的方式,下面是通用的方式

css 变量方法

如果项目里用的不是 less, 那么还是用 css 的方法,通用且容易操作,使用 css 变量来进行主题色的修改,替换主题色变量,然后用 setProperty 来进行动态修改

用法就是给变量加--前缀,涉及到主题色的都改成 var(--themeColor)这种方式

用之前看下兼容性 image.png

兼容性的网址: caniuse.com/#search=CSS…

大部分主流浏览器还是支持的,而且主要是操作起来够简便。 用法举例:

body {
  --themeColor: #000;
}

使用:

.main {
  color: var(--themeColor);
}

要修改主题色的话使用下面的方式:

document.body.style.setProperty("--themeColor", "#ff0000");

判断浏览器是否支持CSS变量的兼容写法

// 判断浏览器是否兼容CSS变量
const isSupported =
  window.CSS && window.CSS.supports && window.CSS.supports("--a", 0);

if (isSupported) {
  /* supported */
  // 设置CSS变量
  let dom = document.querySelector("html");
  dom.style.setProperty("--" + key, this.$store.state.webInfo.style[key]);
} else {
  // 动态引入CSS文件
  let styleStr = "div{width: 100px;}";
  let style = document.createElement("style");
  style.type = "text/css";
  style.rel = "stylesheet";
  //for Chrome Firefox Opera Safari
  style.appendChild(document.createTextNode(styleStr));
  let head = document.getElementsByTagName("head")[0];
  head.appendChild(style);
}