前端换肤通常是对项目主题色进行更换,一般来说换肤需求通常有以下几种情况:
- 提供几种可供选择的颜色主题,用户可对主题进行切换,一般这种主题不会很多。
- 进一步的,提供输入框由用户自定义色值,或者通过取色板取色,可供选择的颜色范围就很大了。
- 但更多的,是标准项目针对不同客户进行定制开发,交付定制的颜色和控件样式主题。
多主题换肤
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";`, // 推荐这种,更为灵活且方便后续维护 }, }, }, }, }, };
复制