vue3 + windicss + css变量换肤以及主题适配

核心内容

  •  主题色替换
    • windicss + css + element-plus
    • windicss + css +element-plus  (兼容windicss opacity 属性)
  • 换肤 vitepress + windicss + css

主题适配效果展示

换肤效果展示:

一: 主题色替换

1. windicss + css + element-plus

步骤一:定义需要覆盖的element-plus 的 css 变量:element-plus官网 css 主题覆盖

:root{
  --el-color-primary: #3434ce;
  --el-color-primary-light-9: #3434ce;
  --el-color-primary-light-8: #4646cc;
  --el-color-primary-light-7: #4a4ace;
  --el-color-primary-light-5: #5252bb;
  --el-color-primary-light-3: #575792;
  --el-button-active-color: #3434ce;
}

windicss 中使用css 变量:配置windi.config.ts

// windi.config.ts
module.exports = {
  theme: {
    extend: {
      colors: {
        primary: "var(--el-color-primary)", // 直接使用element-plus 的主题变量
      },
    },
  },
};

通过 setProperty 修改css变量值

document.documentElement.style.setProperty(varName, varValue);

注意:以上方案可实现windicss使用css 变量实现 主题替换(不能兼容windicss opacity), 但是通常系统中会用到不同透明度的主题色,那么我们使用如上方案就需求定义不同透明度的主题色,然后在windicss中设置不同的颜色值,如element-plus 内置变量:

--el-color-primary: #3434ce;
--el-color-primary-light-9: #3434ce;
--el-color-primary-light-8: #4646cc;
--el-color-primary-light-7: #4a4ace;
--el-color-primary-light-5: #5252bb;
--el-color-primary-light-3: #575792;
--el-button-active-color: #3434ce;

但是实际使用windicss 时,只需要一个主题色, 其他不同透明度的颜色根据opacity来设置 比如:class="bg-primary bg-opacity-10",兼容opacity方案如下:

2. windicss + css +element-plus  (兼容windicss opacity 属性)

rgba:使用透明度时只能识别, 0, 0, 0 ,格式的颜色值, 所以定义css变量不能直接取16位颜色值(可使用方法转换颜色值得到数值: 转换方法附在文章末尾)

:root{
  --wi-color-primary: 52, 52, 206; 
}
// windi.config.ts
function color(variable: string) {
  return (val: any) => {
    if (val.opacityValue === undefined) {
      return `rgb(var(${variable}))`;
    }
    return `rgba(${variable}, ${val.opacityValue})`;
  };
}
module.exports = {
  theme: {
    extend: {
      colors: {
        primary: color("var(--wi-color-primary)"), // 兼容opacity
      },
    },
  },
};

二: 换肤

定义 css 变量以及 多套主题 css 变量值: 覆盖需要替换的 vitepress 内置变量

  
:root {
  --vp-c-bg: #fff;
  --vp-c-text-1: #000;
  --vp-sidebar-bg-color: #f6f6f7;
}
[data-theme="theme1"] {
  --vp-c-bg: #fdfefe;
  --vp-c-text-1: #054b8d;
  --vp-sidebar-bg-color: #e1e8ef;
}
[data-theme="theme2"] {
  --vp-c-bg: #eaeaef;
  --vp-c-text-1: #05058f;
  --vp-sidebar-bg-color: #fff;
}
[data-theme="theme3"] {
  --vp-c-bg: #395e45;
  --vp-c-text-1: #f5f5f5;
  --vp-sidebar-bg-color: #7b8d81;
}

windicss 使用主题变量, .md 案例中windicss 生效必须配置include

module.exports = {
  extract: {
    include: ["**/*.{md,vue}", ".vitepress/**/*.{ts,md,vue}"],
  },
  theme: {
    extend: {
      colors: {
        bg: color("var(--vp-c-bg)"),
        text: color("var( --vp-c-text)"),
        sidebar: color("var(--vp-sidebar-bg-color)"),
      },
    },
  },
};

setAttribute 修改 data-theme 属性值

document.documentElement.setAttribute("data-theme", theme.value);

三:主题替换完整代码:(附加)

<!--
 * @Description: 主题适配案例
 * @Author: ym
 * @Date: 2023-11-23 19:10:44
 * @LastEditTime: 2023-12-05 16:27:09
-->
<template>
  <div class="w-[80%] min-w-[600px] mx-auto p-8">
    <div class="flex justify-end items-center">
      <span class="mr-4">切换主题色</span>
      <el-color-picker v-model="colorThem" @change="onChange"/>
    </div>
    <div class="card border border-primary1 ">
      <div class="title"> windicss 主题色</div>
      <div class="text-primary1">
          测试用标题
      </div>
      <div class="flex py-2">
        <div class="bg-primary1 text-white text-xs p-2 mr-2 rounded">primary</div>
        <div class="border border-primary1 text-primary text-xs p-2 mr-2 rounded">primary</div>
      </div>
    </div>
    <div class="card bg-primary bg-opacity-4 border border-primary border-opacity-55">
      <div class="title"> windicss 主题色(兼容opacity)</div>
      <div class="text-primary">
          测试用标题
      </div>
      <div class="text-primary text-opacity-70">
        测试用标题
      </div>
      <div class="text-primary text-opacity-50">
        测试用标题
      </div>
      <div class="flex py-2">
        <div class="bg-primary bg-opacity-80 text-white text-xs p-2 mr-2 rounded">primary</div>
        <div class="bg-primary bg-opacity-50 text-white text-xs p-2 mr-2 rounded">primary</div>
        <div class="border border-primary border-opacity-90 text-primary  text-xs p-2 mr-2 rounded">primary</div>
        <div class="border border-primary border-opacity-50 text-primary text-opacity-50 text-xs p-2 mr-2 rounded">primary</div>
      </div>
    </div>
    <div class="card bg-primary bg-opacity-3 border border-primary border-opacity-35 ">
      <div class="title"> element-plus 主题色</div>
      <div>
        <el-button type="primary" disabled>Primary</el-button>
        <el-button type="primary" plain disabled>Primary</el-button>
        <el-button type="primary">Primary</el-button>
        <el-button type="primary" link>Primary</el-button>
        <el-button type="primary" text>Primary</el-button>
      </div>
      <div>
        <el-progress class="py-2" :text-inside="true" :stroke-width="26" :percentage="70" />
        <el-progress
        class="py-2"
          :text-inside="true"
          :stroke-width="24"
          :percentage="100"
          status="success"
        />
      </div>
    </div>
    
  </div>
  
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useCssVar, newColorWithOpacity } from './cssVar'
const themConfig: {[key: string]: number} = {
  '--el-color-primary': 1,
  '--el-color-primary-light-9': 0.9,
  '--el-color-primary-light-8': 0.8,
  '--el-color-primary-light-7': 0.7,
  '--el-color-primary-light-5': 0.5,
  '--el-color-primary-light-3': 0.3,
  '--el-button-active-color': 0.9,
}
const { setCssVar } = useCssVar('--el-color-primary', '#3434ce');
const colorThem = ref('#3434ce')
const onChange = (val: string) => {
  Object.keys(themConfig).forEach(e => {
    if (themConfig[e] === 1) {
      setCssVar(e, val)
      setCssVar('--wi-color-primary', newColorWithOpacity(val, 1, true))
    } else {
      const _color = newColorWithOpacity(val, themConfig[e])
      setCssVar(e, _color)
    }
  })
}
</script>
<style lang="scss" scoped>
.card{
  @apply my-4  px-8 pb-8 rounded 
}
.title{
  @apply text-lg py-4

}
</style>
// cssVar.ts
import { reactive, watch } from "vue";

export function useCssVar(varName: string, initialValue: string) {
  const cssVars = reactive({
    [varName]: initialValue,
  });

  const setCssVar = (varName: string, value: string) => {
    cssVars[varName] = value;
    document.documentElement.style.setProperty(varName, value);
  };

  return {
    cssVars,
    setCssVar,
  };
}
/**
 * 将16进制的色值,根据透明度换算为新的颜色值
 * @param color 16位的色值
 * @param opacity 透明度
 * @param isRgbNumber 是否为rgba色值
 */
export const newColorWithOpacity = (color: string, opacity: number, isRgbNumber = false) => {
  const hex = color.replace(/^#/, "");
  const r = parseInt(hex.substr(0, 2), 16);
  const g = parseInt(hex.substr(2, 2), 16);
  const b = parseInt(hex.substr(4, 2), 16);

  // 计算新的RGB值
  const newR = Math.round((1 - opacity) * 255 + opacity * r);
  const newG = Math.round((1 - opacity) * 255 + opacity * g);
  const newB = Math.round((1 - opacity) * 255 + opacity * b);

  // 将新的RGB值转换为16位颜色值
  const newColor = `#${newR.toString(16).padStart(2, "0")}${newG.toString(16).padStart(2, "0")}${newB
    .toString(16)
    .padStart(2, "0")}`;

  return isRgbNumber ? `${newR},${newG},${newB}` : newColor;
};

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值