feat(projects): 集成naiveUI主题配置,将css vars添加至html

This commit is contained in:
Soybean
2022-01-04 02:20:32 +08:00
parent 0d2a5629e8
commit 2c196841bd
12 changed files with 297 additions and 88 deletions

View File

@@ -1,6 +1,19 @@
<template>
<router-view />
<n-config-provider :theme-overrides="theme.naiveThemeOverrides">
<router-view />
</n-config-provider>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
import { onMounted } from 'vue';
import { NConfigProvider } from 'naive-ui';
import { useThemeStore } from '@/store';
const theme = useThemeStore();
const { addThemeCssVarsToRoot } = useThemeStore();
onMounted(() => {
addThemeCssVarsToRoot();
});
</script>
<style scoped></style>

View File

@@ -1,74 +0,0 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
position: relative;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition: color 0.5s, background-color 0.5s;
line-height: 1.6;
font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69" xmlns:v="https://vecta.io/nano"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

Before

Width:  |  Height:  |  Size: 308 B

View File

@@ -4,10 +4,6 @@ import { setupRouter } from '@/router';
import { setupStore } from '@/store';
import App from './App.vue';
function setupPlugins() {
setupAssets();
}
async function setupApp() {
const app = createApp(App);
@@ -21,5 +17,6 @@ async function setupApp() {
app.mount('#app');
}
setupPlugins();
setupAssets();
setupApp();

View File

@@ -0,0 +1,40 @@
import { colord } from 'colord';
import { getColorPalette } from '@/utils';
type ColorType = 'primary' | 'info' | 'success' | 'warning' | 'error';
type ColorScene = '' | 'Suppl' | 'Hover' | 'Pressed' | 'Active';
type ColorKey = `${ColorType}Color${ColorScene}`;
type ThemeColor = {
[key in ColorKey]?: string;
};
interface ColorAction {
scene: ColorScene;
handler: (color: string) => string;
}
/** 获取主题颜色的各种场景对应的颜色 */
export function getThemeColors(colors: [ColorType, string][]) {
const colorActions: ColorAction[] = [
{ scene: '', handler: color => color },
{ scene: 'Suppl', handler: color => color },
{ scene: 'Hover', handler: color => getColorPalette(color, 5) },
{ scene: 'Pressed', handler: color => getColorPalette(color, 7) },
{ scene: 'Active', handler: color => colord(color).alpha(0.1).toHex() }
];
const themeColor: ThemeColor = {};
colors.forEach(color => {
colorActions.forEach(action => {
const [colorType, colorValue] = color;
const colorKey: ColorKey = `${colorType}Color${action.scene}`;
themeColor[colorKey] = action.handler(colorValue);
});
});
return themeColor;
}

View File

@@ -1,7 +1,87 @@
import { ref, computed } from 'vue';
import type { Ref, ComputedRef } from 'vue';
import { defineStore } from 'pinia';
import { useThemeVars } from 'naive-ui';
import type { GlobalThemeOverrides } from 'naive-ui';
import { kebabCase } from 'lodash-es';
import { getColorPalette } from '@/utils';
import { getThemeColors } from './helpers';
// interface ThemeStore {
// primary: string;
// }
interface OtherColor {
/** 信息 */
info: string;
/** 成功 */
success: string;
/** 警告 */
warning: string;
/** 错误 */
error: string;
}
export const useThemeStore = defineStore('theme-store', () => {});
interface ThemeStore {
/** 主题颜色 */
themeColor: Ref<string>;
/** 其他颜色 */
otherColor: ComputedRef<OtherColor>;
/** naiveUI的主题配置 */
naiveThemeOverrides: ComputedRef<GlobalThemeOverrides>;
/** 添加css vars至html */
addThemeCssVarsToRoot(): void;
}
type ThemeVarsKeys = keyof Exclude<GlobalThemeOverrides['common'], undefined>;
export const useThemeStore = defineStore('theme-store', () => {
const themeVars = useThemeVars();
const themeColor = ref('#1890ff');
const otherColor = computed<OtherColor>(() => ({
info: getColorPalette(themeColor.value, 7),
success: '#52c41a',
warning: '#faad14',
error: '#f5222d'
}));
const naiveThemeOverrides = computed<GlobalThemeOverrides>(() => {
const { info, success, warning, error } = otherColor.value;
const themeColors = getThemeColors([
['primary', themeColor.value],
['info', info],
['success', success],
['warning', warning],
['error', error]
]);
const colorLoading = themeColor.value;
return {
common: {
...themeColors
},
LoadingBar: {
colorLoading
}
};
});
function addThemeCssVarsToRoot() {
const updatedThemeVars = { ...themeVars.value };
Object.assign(updatedThemeVars, naiveThemeOverrides.value.common);
const keys = Object.keys(updatedThemeVars) as ThemeVarsKeys[];
const style: string[] = [];
keys.forEach(key => {
style.push(`--${kebabCase(key)}: ${updatedThemeVars[key]}`);
});
const styleStr = style.join(';');
document.documentElement.style.cssText += styleStr;
}
const themeStore: ThemeStore = {
themeColor,
otherColor,
naiveThemeOverrides,
addThemeCssVarsToRoot
};
return themeStore;
});

116
src/utils/common/color.ts Normal file
View File

@@ -0,0 +1,116 @@
import { colord } from 'colord';
import type { HsvColor } from 'colord';
type ColorIndex = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;
const hueStep = 2;
const saturationStep = 16;
const saturationStep2 = 5;
const brightnessStep1 = 5;
const brightnessStep2 = 15;
const lightColorCount = 5;
const darkColorCount = 4;
/**
* 根据颜色获取调色板颜色(从左至右颜色从浅到深6为主色号)
* @param color - 颜色
* @param index - 调色板的对应的色号(6为主色号)
* @description 算法实现从ant-design调色板算法中借鉴 https://github.com/ant-design/ant-design/blob/master/components/style/color/colorPalette.less
*/
export function getColorPalette(color: string, index: ColorIndex) {
if (index === 6) return color;
const isLight = index < 6;
const hsv = colord(color).toHsv();
const i = isLight ? lightColorCount + 1 - index : index - lightColorCount - 1;
const newHsv: HsvColor = {
h: getHue(hsv, i, isLight),
s: getSaturation(hsv, i, isLight),
v: getValue(hsv, i, isLight)
};
return colord(newHsv).toHex();
}
/**
* 根据颜色获取调色板颜色所有颜色
* @param color - 颜色
*/
export function getAllColorPalette(color: string) {
const indexs: ColorIndex[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
return indexs.map(index => getColorPalette(color, index));
}
/**
* 获取色相渐变
* @param hsv - hsv格式颜色值
* @param i - 与6的相对距离
* @param isLight - 是否是亮颜色
*/
function getHue(hsv: HsvColor, i: number, isLight: boolean) {
let hue: number;
if (hsv.h >= 60 && hsv.h <= 240) {
// 冷色调
// 减淡变亮 色相顺时针旋转 更暖
// 加深变暗 色相逆时针旋转 更冷
hue = isLight ? hsv.h - hueStep * i : hsv.h + hueStep * i;
} else {
// 暖色调
// 减淡变亮 色相逆时针旋转 更暖
// 加深变暗 色相顺时针旋转 更冷
hue = isLight ? hsv.h + hueStep * i : hsv.h - hueStep * i;
}
if (hue < 0) {
hue += 360;
} else if (hue >= 360) {
hue -= 360;
}
return hue;
}
/**
* 获取饱和度渐变
* @param hsv - hsv格式颜色值
* @param i - 与6的相对距离
* @param isLight - 是否是亮颜色
*/
function getSaturation(hsv: HsvColor, i: number, isLight: boolean) {
let saturation: number;
if (isLight) {
saturation = hsv.s - saturationStep * i;
} else if (i === darkColorCount) {
saturation = hsv.s + saturationStep;
} else {
saturation = hsv.s + saturationStep2 * i;
}
if (saturation > 100) {
saturation = 100;
}
if (isLight && i === lightColorCount && saturation > 10) {
saturation = 10;
}
if (saturation < 6) {
saturation = 6;
}
return saturation;
}
/**
* 获取明度渐变
* @param hsv - hsv格式颜色值
* @param i - 与6的相对距离
* @param isLight - 是否是亮颜色
*/
function getValue(hsv: HsvColor, i: number, isLight: boolean) {
let value: number;
if (isLight) {
value = hsv.v + brightnessStep1 * i;
} else {
value = hsv.v - brightnessStep2 * i;
}
if (value > 100) {
value = 100;
}
return value;
}

View File

@@ -1,3 +1,4 @@
export * from './typeof';
export * from './console';
export * from './color';
export * from './design-pattern';

View File

@@ -1,5 +1,7 @@
<template>
<div></div>
<div>
<h3 class="text-primary">Login</h3>
</div>
</template>
<script setup lang="ts"></script>