前端换肤通常是对项目主题色进行更换,一般来说换肤需求通常有以下几种情况:
- 提供几种可供选择的颜色主题,用户可对主题进行切换,一般这种主题不会很多。
- 进一步的,提供输入框由用户自定义色值,或者通过取色板取色,可供选择的颜色范围就很大了。
- 但更多的,是标准项目针对不同客户进行定制开发,交付定制的颜色和控件样式主题。
多主题换肤
JS切换全局class
这里举例小说网站常见的夜间模式的切换。
- 首先,我们先在项目中建立个 day.less 文件,这是我们通常打开网站见到的网站样式。
- 然后,我们再在项目中建立个 night.less 文件,这个样式展示的也即夜间模式。
- 到这,我们可以通过js来控制样式的切换。
如下是代码示例:
<template>
<div :class="containerClass">
<input type="button" :value="isDayMode ? '日间模式' : '夜间模式'" @click="isDayMode = !isDayMode" />
......
</div>
</template>
export default {
data() {
return {
isDayMode: true, // 默认日间模式
}
},
computed: {
containerClass() {
return this.isDayMode ? 'day-container' : 'night-container'
}
}
}
<style scoped lang="less">
@import './styles/day.less';
@import './styles/night.less';
</style>
day.less
.day-container {
background: white!important;
color: black!important;
}
night.less
.night-container {
background: black!important;
color: white!important;
}
JS切换href属性值
在没有使用构建工具的项目中,可以使用 JS 切换 href 属性值,进而实现换肤功能,本质上与上面所述方法是一致的。
<link id="containerClass" href="day.css" rel="stylesheet" type="text/css">
document.querySelector('#containerClass').href = 'night.css'
css 变量实现动态色值换肤
如果要实现更灵活的动态换肤,比如自定义色值或使用从取色板上获取的颜色进行主题色的更换,那上面的方式就不适合了。
通过定义全局主题色 css 变量,方便各模块都能通过 var()
使用主题色变量,再用 setProperty
对全局主题色变量值进行动态修改。
先在顶级元素上设置css全局变量,用法就是给变量加 --
前缀。
body {
--themeColor:#000;
}
涉及到主题色的都改成 var(--themeColor)
这种方式
.main {
color: var(--themeColor);
}
修改主题色
document.body.style.setProperty('--themeColor', '#ff0000');
可以看下兼容性,大部分主流浏览器还是支持的,而且操作起来够简便。
组件库换肤方式
实际业务中,交付的项目更多的是对所用到的组件进行颜色和样式定制,实际是对项目所用的组件库进行主题上的定制。那我们分析下业务上常用的组件库,如何进行主题上的定制。
Element-UI 定制主题
Element 默认提供一套主题,CSS 命名采用 BEM 的风格,方便使用者在项目中改变 SCSS 变量进行样式覆盖。
Element 的 theme-chalk 使用 SCSS 编写,如果你的项目也使用了 SCSS,那么可以直接在项目中改变 Element 的样式变量。
新建一个样式文件,例如 element-variables.scss,写入以下内容:
/* 改变主题色变量 */
$--color-primary: orange;
/* 改变 icon 字体路径变量,必需 */
$--font-path: '~element-ui/lib/theme-chalk/fonts';
@import "~element-ui/packages/theme-chalk/src/index";
上面只做了主题色的变更,如果你对其他相关颜色变量也有改动的需求,可进行变量覆盖。
注意:此处变量的覆盖需要在引入 element-ui 默认样式的前面。因为默认变量添加了 !default
属性,则当变量已经赋值后,则不进行值的替换。
如果还需要对组件进行样式上的定制,则可以翻阅主题样式文件:element-ui/packages/theme-chalk/src/common/var.scss
将需要定制的主题样式变量名复制到你的文件上,进行重写赋值。
/* 改变主题色变量 */
$--color-primary: orange;
/* 改变组件样式 */
$--button-border-radius: 10px;
之后,在项目的入口文件中,直接引入以上样式文件即可(无需引入 Element 编译好的 CSS 文件):
import Vue from 'vue'
import Element from 'element-ui'
import './element-variables.scss'
Vue.use(Element)
需要注意的是,覆盖字体路径变量是必需的,将其赋值为 Element 中 icon 图标所在的相对路径即可。
Ant Design Vue 定制主题
Ant Design 设计规范上支持一定程度的样式定制,以满足业务和品牌上多样化的视觉需求,包括但不限于主色、圆角、边框和部分组件的视觉定制。
antd 的样式使用了 Less 作为开发语言,并定义了一系列全局/组件的样式变量,你可以根据需求进行相应调整。
以下是一些最常用的通用变量,所有样式变量可以在 这里 找到。
@primary-color: #1890ff; // 全局主色
@link-color: #1890ff; // 链接色
@success-color: #52c41a; // 成功色
@warning-color: #faad14; // 警告色
@error-color: #f5222d; // 错误色
@font-size-base: 14px; // 主字号
@heading-color: rgba(0, 0, 0, 0.85); // 标题色
@text-color: rgba(0, 0, 0, 0.65); // 主文本色
@text-color-secondary: rgba(0, 0, 0, 0.45); // 次文本色
@disabled-color: rgba(0, 0, 0, 0.25); // 失效色
@border-radius-base: 4px; // 组件/浮层圆角
@border-color-base: #d9d9d9; // 边框色
@box-shadow-base: 0 2px 8px rgba(0, 0, 0, 0.15); // 浮层阴影
我们使用 modifyVars 的方式来进行覆盖变量。下面将针对不同的场景提供一些常用的定制方式。
在构建工具中定制主题
项目根目录下新建文件 vue.config.js(这里只举例了这种方式),其他方式可以参阅定制主题
在 webpack 配置里开启,这里使用 less@3.13.1
less-loader@6.2.0
module.exports = {
css: {
loaderOptions: {
less: {
lessOptions: {
// If you are using less-loader@5 please spread the lessOptions to options directly
modifyVars: {
'primary-color': '#1DA57A',
'link-color': '#1DA57A',
'border-radius-base': '2px',
},
javascriptEnabled: true,
},
},
},
},
};
在 main.js 中进行引入,此处注意这里的 css 样式文件为 less 格式,以在构建时进行变量的覆盖。
import Antd from 'ant-design-vue';
import 'ant-design-vue/dist/antd.less'
Vue.use(Antd);
配置 less 变量文件
另外一种方式是建立一个单独的 less 变量文件,引入这个文件覆盖 antd.less 里的变量。
@import '~ant-design-vue/dist/antd.less'; // 引入官方提供的 less 样式入口文件
@import 'your-theme-file.less'; // 你自己的主题样式文件,用于覆盖上面定义的变量
注意:
- 注意样式必须加载 less 格式,一个常见的问题就是引入了多份样式,less 的样式被 css 的样式覆盖了,如果你是通过 ‘ant-design-vue/dist/antd.css’ 引入样式的,改为 ‘ant-design-vue/dist/antd.less’。
- 这里进行变量的覆盖与 Element-UI 的处理顺序是不一样的,因为这里的变量是按照前后顺序进行覆盖的。默认 ant-design-vue 变量值并没有进行特殊处理。
- 这种方式已经载入了所有组件的样式,不需要也无法和按需加载插件
babel-plugin-import
的 style 属性一起使用。如果你在使用babel-plugin-import
的style
配置来引入样式,需要将配置值从 ‘css’ 改为 true,这样会引入 less 文件。
Vant 定制主题
Vant 提供了一套默认主题,CSS 命名采用 BEM 的风格,方便使用者覆盖样式。
Vant 使用了 Less 对样式进行预处理,并内置了一些样式变量。
完整的内置变量的文件路径为:node_modules/vant/es/style/var.less
或者 node_modules/vant/lib/style/var.less
,两文件内容一致。
如下进行部分变量的罗列:
// Color Palette
@black: #000;
@white: #fff;
@gray-1: #f7f8fa;
@gray-2: #f2f3f5;
@gray-3: #ebedf0;
@gray-4: #dcdee0;
@gray-5: #c8c9cc;
@gray-6: #969799;
@gray-7: #646566;
@gray-8: #323233;
@red: #ee0a24;
@blue: #1989fa;
@orange: #ff976a;
@orange-dark: #ed6a0c;
@orange-light: #fffbe8;
@green: #07c160;
// Gradient Colors
@gradient-red: linear-gradient(to right, #ff6034, #ee0a24);
@gradient-orange: linear-gradient(to right, #ffd01e, #ff8917);
// Component Colors
@text-color: @gray-8;
@active-color: @gray-2;
@active-opacity: 0.7;
@disabled-opacity: 0.5;
@background-color: @gray-1;
@background-color-light: #fafafa;
@text-link-color: #576b95;
// Padding
@padding-base: 4px;
@padding-xs: @padding-base * 2;
@padding-sm: @padding-base * 3;
@padding-md: @padding-base * 4;
@padding-lg: @padding-base * 6;
@padding-xl: @padding-base * 8;
// Font
@font-size-xs: 10px;
@font-size-sm: 12px;
@font-size-md: 14px;
@font-size-lg: 16px;
@font-weight-bold: 500;
@line-height-xs: 14px;
@line-height-sm: 18px;
@line-height-md: 20px;
@line-height-lg: 22px;
@base-font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue',
Helvetica, Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB',
'Microsoft Yahei', sans-serif;
@price-integer-font-family: Avenir-Heavy, PingFang SC, Helvetica Neue, Arial,
sans-serif;
// Animation
@animation-duration-base: 0.3s;
@animation-duration-fast: 0.2s;
@animation-timing-function-enter: ease-out;
@animation-timing-function-leave: ease-in;
// Border
@border-color: @gray-3;
@border-width-base: 1px;
@border-radius-sm: 2px;
@border-radius-md: 4px;
@border-radius-lg: 8px;
@border-radius-max: 999px;
通过替换样式变量即可定制你自己需要的主题。
引入样式源文件
定制主题时,首先需要引入组件对应的 Less 样式文件,支持按需引入和手动引入两种方式。
- 按需引入样式(推荐)
按需引入首先需要安装插件:npm i babel-plugin-import -D
babel-plugin-import
是一款 babel 插件,它会在编译过程中将 import
的写法自动转换为按需引入的方式。
在 babel.config.js 中配置按需引入样式源文件,注意 babel6 不支持按需引入样式,请手动引入样式。
module.exports = {
plugins: [
[
'import',
{
libraryName: 'vant',
libraryDirectory: 'es',
style: (name) => `${name}/style/less`, // 自动处理组件样式路径,这里的name是组价名
},
'vant',
],
],
};
- 手动引入样式(不推荐)
// 引入全部样式
import 'vant/lib/index.less';
// 引入单个组件样式
import 'vant/lib/button/style/less';
修改样式变量
使用 Less 提供的 modifyVars
即可对变量进行修改,下面是参考的 webpack 配置。
// webpack.config.js
module.exports = {
rules: [
{
test: /\.less$/,
use: [
// ...其他 loader 配置
{
loader: 'less-loader',
options: {
// 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。
lessOptions: {
modifyVars: {
// 直接覆盖变量
// 'text-color': '#111',
// 'border-color': '#eee',
// 或者可以通过 less 文件覆盖(文件路径为绝对路径)
hack: `true; @import "your-less-file-path.less";`, // 推荐这种,更为灵活且方便后续维护
},
},
},
},
],
},
],
};
如果 vue-cli 搭建的项目,可以在 vue.config.js 中进行配置。
// vue.config.js
module.exports = {
css: {
loaderOptions: {
less: {
// 若 less-loader 版本小于 6.0,请移除 lessOptions 这一级,直接配置选项。
lessOptions: {
modifyVars: {
// 直接覆盖变量
// 'text-color': '#111',
// 'border-color': '#eee',
// 或者可以通过 less 文件覆盖(文件路径为绝对路径)
hack: `true; @import "your-less-file-path.less";`, // 推荐这种,更为灵活且方便后续维护
},
},
},
},
},
};