【sass+css变量实现换肤】

文章目录

需求

假如你最近在开发一套组件库,如何让用户可以换肤呢?

  1. 使用者可以固定自定义主题色、基本色等相关主题
  2. 也支持动态切换,可以只切换单个颜色,也支持全部切换

思路

定义sass或者css变量,所有的颜色都用变量定义,当用户自定义颜色时,通过覆盖或者修改变量完成换肤

组件库css样式设计注意点:

  1. 用bem方式命名:块、元素、修饰符
  2. 结构和皮肤分离

实现

组件

这里拿两个组件举例子,一个是src/myui/mybutton.vue:

html 复制代码
<template>
  <button class="button button-primary">
    <slot></slot>
  </button>
</template>
<script>
export default {
  name: "mybutton",
};
</script>

另一个是src/myui/myheader.vue:

html 复制代码
<template>
<!-- 定义结构和皮肤的类分离开 -->
  <header class="header background-theme">12312</header>
</template>

 
<script>
export default {
  name: "myheader",
};
</script>

手动换肤

定义默认的颜色变量文件src/myui/css/variable.scss:

css 复制代码
$theme-color:red !default;// 默认皮肤都要加default,如果不加default,后面可能无法覆盖这个变量
$button-primary:blue !default;

在main.js中引入默认样式:

html 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import myui from "./myui"
import "./variable.scss"
createApp(App).use(myui).mount('#app')

使用:

html 复制代码
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
import TheWelcome from "./components/TheWelcome.vue";
import { onMounted, ref } from "vue";
</script>

<template>
  <div class="myclass">
    <myheader></myheader>
    <mybutton>1231</mybutton>
  </div>
</template>

<style scoped>
header {
  line-height: 1.5;
}

.logo {
  display: block;
  margin: 0 auto 2rem;
}

@media (min-width: 1024px) {
  header {
    display: flex;
    place-items: center;
    padding-right: calc(var(--section-gap) / 2);
  }

  .logo {
    margin: 0 2rem 0 0;
  }

  header .wrapper {
    display: flex;
    place-items: flex-start;
    flex-wrap: wrap;
  }
}
</style>

src/myui/css/index.scss中定义样式:

css 复制代码
 @import "./variable.scss";// 引入
 .header{
  width: 100%;
  min-height: 30px;
 }
 .button{
  min-width: 40px;
 }
 .background-theme{
   background-color: $theme-color;//使用变量
 }
 .button-primary{
  background-color: $button-primary;
 }

如果是手动换肤,接下来可以新建一个新皮肤的scss文件src/myindex.scss:

css 复制代码
// 编写变量来覆盖变量
$theme-color:pink;
$button-primary:yellow;

@import "@/myui/css/index.scss"// 引入组件库的样式文件,一定要写在最后

然后改变main.js中的引用:

javascript 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import myui from "./myui"
import "./myindex.scss"// 改变引用
createApp(App).use(myui).mount('#app')

动态换肤

如果想实现动态换肤,那么需要改写变量和属性的生成方式,src/myui/css/variable.scss的内容改为:

css 复制代码
//  $theme-color:red !default;// 默认皮肤都要加default,如果不加default,后面可能无法覆盖这个变量
//  $button-primary:blue !default;

// 将变量的声明方式改写成集合的形式
$themes:(// 在scss中,()用来定义对象
    default-theme:(// default-theme是这个对象的属性,这个属性的值也是一个对象
        //这一套主题所有的相关颜色
        theme-color:red,
        button-primary:blue
    )
)!default;

样式的生成通过方法来生成,方法定义在src/myui/css/mixin.scss:

css 复制代码
@mixin get-backcolor($color){// 在scss中,通过@mixin定义一个方法
   // scss中使用@each进行循环,这里循环variable.scss中的$themes对象,因此$themename指default-theme,$theme为default-theme的值
   @each $themename,$theme in $themes {
      [data-skin='#{$themename}'] &{// 如果html标签上面的data-skin属性等于主题的名字,就为当前元素的background-color设置某个颜色,颜色的获取通过map-get方法获取
         // 就是通过map对象获取值,map对象就是default-theme的值,$color是这个方法传进来的变量,但实际上是default-theme的值(对象)的键,
         // 所以map-get($theme,$color)取的是:default-theme的值(对象)的$color指向的键的值
         background-color: map-get($theme,$color);
      }
   }
}
// 如果要写完整的话,还需要定义生成字体颜色的方法,但是本文用生成背景颜色的方法作为示例
// @mixin get-fontcolor(){

// }

之后属性由方法来生成,src/myui/css/index.scss中定义样式:

css 复制代码
 @import "./variable.scss";
 @import "./mixin.scss";
 .header{
  width: 100%;
  min-height: 30px;
 }
 .button{
  min-width: 40px;
 }
 .background-theme{
   @include get-backcolor('theme-color')// 在scss中使用@include调用方法
 }
 .button-primary{
   @include get-backcolor('button-primary')
 }

为根节点设置data-skin属性,在src/App.vue的script标签中添加代码:

javascript 复制代码
onMounted(() => {
  window.document.documentElement.setAttribute('data-skin','default- theme')
})

在src/myindex.scss中自定义两套皮肤:

css 复制代码
$themes:(
    skin1:(
        theme-color:var(--data-theme-color,pink),
        button-primary:var(--data-button-primary,blue),
    ),
    skin2:(
        theme-color:var(--data-theme-color,black),
        button-primary:var(--data-button-primary,green),
    )
);
@import "@/myui/css/index.scss"

在src/App.vue中添加按钮,点击后进行皮肤切换

html 复制代码
<script setup>
import HelloWorld from "./components/HelloWorld.vue";
import TheWelcome from "./components/TheWelcome.vue";
import { onMounted, ref } from "vue";
onMounted(() => {
  window.document.documentElement.setAttribute('data-skin','skin1')
})
function changeskin(name) {
  window.document.documentElement.setAttribute('data-skin',name)
 }
</script>

<template>
  <div>
      <button @click="changeskin('skin1')">皮肤1</button>
      <button @click="changeskin('skin2')">皮肤2</button>
      <myheader></myheader>
      <mybutton>1231</mybutton>
  </div>
</template>

<style scoped>
header {
  line-height: 1.5;
}

.logo {
  display: block;
  margin: 0 auto 2rem;
}

@media (min-width: 1024px) {
  header {
    display: flex;
    place-items: center;
    padding-right: calc(var(--section-gap) / 2);
  }

  .logo {
    margin: 0 2rem 0 0;
  }

  header .wrapper {
    display: flex;
    place-items: flex-start;
    flex-wrap: wrap;
  }
}
</style>

这样,我们就利用scss完成了换肤:首先借助scss的方法,修改变量的定义的形式,通过方法生成样式的属性,根据html上的属性名来决定用哪一套变量,从而生成不同的颜色。接下来配合css变量

相关推荐
White graces4 分钟前
正则表达式效验邮箱格式, 手机号格式, 密码长度
前端·spring boot·spring·正则表达式·java-ee·maven·intellij-idea
庸俗今天不摸鱼4 分钟前
Canvas进阶-4、边界检测(流光,鼠标拖尾)
开发语言·前端·javascript·计算机外设
bubusa~>_<28 分钟前
解决npm install 出现error,比如:ERR_SSL_CIPHER_OPERATION_FAILED
前端·npm·node.js
流烟默1 小时前
vue和微信小程序处理markdown格式数据
前端·vue.js·微信小程序
梨落秋溪、1 小时前
输入框元素覆盖冲突
java·服务器·前端
菲力蒲LY2 小时前
vue 手写分页
前端·javascript·vue.js
天下皆白_唯我独黑2 小时前
npm 安装扩展遇到证书失效解决方案
前端·npm·node.js
~欸嘿2 小时前
Could not download npm for node v14.21.3(nvm无法下载节点v14.21.3的npm)
前端·npm·node.js
化作繁星3 小时前
React 高阶组件的优缺点
前端·javascript·react.js
zpjing~.~3 小时前
vue 父组件和子组件中v-model和props的使用和区别
前端·javascript·vue.js