chore(stage-web): make the live2d shadow also follow theme color (#63)

This commit is contained in:
LemonNeko
2025-03-09 21:42:44 +08:00
committed by GitHub
parent b089468783
commit 32a4a86476
10 changed files with 117 additions and 49 deletions
+2
View File
@@ -55,6 +55,7 @@
"@xsai/shared-chat": "catalog:",
"@xsai/stream-text": "catalog:",
"@xsai/utils-chat": "catalog:",
"culori": "^4.0.1",
"defu": "^6.1.4",
"drizzle-kit": "^0.30.5",
"drizzle-orm": "^0.40.0",
@@ -103,6 +104,7 @@
"@proj-airi/unplugin-download": "workspace:^",
"@proj-airi/unplugin-live2d-sdk": "workspace:^",
"@shikijs/markdown-it": "^3.1.0",
"@types/culori": "^2.1.1",
"@types/markdown-it-link-attributes": "^3.0.5",
"@types/nprogress": "^0.2.3",
"@types/three": "^0.174.0",
+2 -6
View File
@@ -3,7 +3,7 @@ import { useSettings } from '@proj-airi/stage-ui/stores/settings'
import { StageTransitionGroup } from '@proj-airi/ui-transitions'
import { useDark } from '@vueuse/core'
import { storeToRefs } from 'pinia'
import { onMounted, watch } from 'vue'
import { watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { RouterView } from 'vue-router'
@@ -17,11 +17,7 @@ watch(settings.language, () => {
watch(settings.themeColorsHue, () => {
document.documentElement.style.setProperty('--theme-colors-hue', settings.themeColorsHue.value.toString())
})
onMounted(() => {
document.documentElement.style.setProperty('--theme-colors-hue', settings.themeColorsHue.value.toString())
})
}, { immediate: true })
</script>
<template>
@@ -11,7 +11,7 @@ https://www.magicpattern.design/tools/css-backgrounds
*/
.cross-background-container {
background-color: #ffffff;
--cross-color: oklch(0.95 0.06 var(--theme-colors-hue));
--cross-color: oklch(0.85 var(--theme-colors-chroma) var(--theme-colors-hue) / 0.2);
background:
radial-gradient(circle, transparent 20%, #ffffff 20%, #ffffff 80%, transparent 80%, transparent),
radial-gradient(circle, transparent 20%, #ffffff 20%, #ffffff 80%, transparent 80%, transparent) 25px 25px,
@@ -26,11 +26,12 @@ https://www.magicpattern.design/tools/css-backgrounds
.dark .cross-background-container {
background-color: #121212;
--cross-color: oklch(0.2 var(--theme-colors-chroma) var(--theme-colors-hue));
background:
radial-gradient(circle, transparent 20%, #121212 20%, #121212 80%, transparent 80%, transparent),
radial-gradient(circle, transparent 20%, #121212 20%, #121212 80%, transparent 80%, transparent) 25px 25px,
linear-gradient(#312129 2px, transparent 2px) 0 -1px,
linear-gradient(90deg, #312129 2px, #121212 2px) -1px 0;
linear-gradient(var(--cross-color) 2px, transparent 2px) 0 -1px,
linear-gradient(90deg, var(--cross-color) 2px, #121212 2px) -1px 0;
background-size:
50px 50px,
50px 50px,
@@ -120,14 +120,14 @@ onAfterSend(async () => {
<fieldset flex="~ row" w-fit rounded-lg>
<label
:class="[
tab === 'chat' ? 'bg-primary-100 dark:bg-[#3c2632]' : 'bg-white dark:bg-[#121212]',
tab === 'chat' ? 'bg-primary-100 dark:bg-primary-900' : 'bg-white dark:bg-primary-950',
tab === 'chat' ? 'text-primary-500 dark:text-primary-500' : '',
]"
flex="~ row"
:checked="tab === 'chat'"
:aria-checked="tab === 'chat'"
border="solid l-2 t-2 b-2 primary-100 dark:[#3c2632]"
bg="hover:primary-100 dark:hover:[#3c2632]"
border="solid l-2 t-2 b-2 primary-100 dark:primary-900"
bg="hover:primary-100 dark:hover:primary-900"
text="primary-300 hover:primary-500 dark:primary-300/50 dark:hover:primary-500"
transition="all duration-250 ease-in-out"
cursor-pointer items-center gap-1 rounded-l-lg px-2
@@ -140,14 +140,14 @@ onAfterSend(async () => {
</label>
<label
:class="[
tab === 'custom' ? 'bg-primary-100 dark:bg-[#3c2632]' : 'bg-white dark:bg-[#121212]',
tab === 'custom' ? 'bg-primary-100 dark:bg-primary-900' : 'bg-white dark:bg-primary-950',
tab === 'custom' ? 'text-primary-500 dark:text-primary-500' : '',
]"
flex="~ row"
:checked="tab === 'custom'"
:aria-checked="tab === 'custom'"
border="solid t-2 b-2 primary-100 dark:[#3c2632]"
bg="hover:primary-100 dark:hover:[#3c2632]"
border="solid t-2 b-2 primary-100 dark:primary-900"
bg="hover:primary-100 dark:hover:primary-900"
text="primary-300 hover:primary-500 dark:primary-300/50 dark:hover:primary-500"
transition="all duration-250 ease-in-out"
cursor-pointer items-center gap-1 px-2
@@ -160,14 +160,14 @@ onAfterSend(async () => {
</label>
<label
:class="[
tab === 'clothes' ? 'bg-primary-100 dark:bg-[#3c2632]' : 'bg-white dark:bg-[#121212]',
tab === 'clothes' ? 'bg-primary-100 dark:bg-primary-900' : 'bg-white dark:bg-primary-950',
tab === 'clothes' ? 'text-primary-500 dark:text-primary-500' : '',
]"
flex="~ row"
:checked="tab === 'clothes'"
:aria-checked="tab === 'clothes'"
border="solid r-2 t-2 b-2 primary-100 dark:[#3c2632]"
bg="hover:primary-100 dark:hover:[#3c2632]"
border="solid r-2 t-2 b-2 primary-100 dark:primary-900"
bg="hover:primary-100 dark:hover:primary-900"
text="primary-300 hover:primary-500 dark:primary-300/50 dark:hover:primary-500"
transition="all duration-250 ease-in-out"
cursor-pointer items-center gap-1 rounded-r-lg px-2
@@ -184,7 +184,7 @@ onAfterSend(async () => {
flex="~ col"
border="solid 4 primary-100 dark:primary-400/20"
h-full w-full overflow-scroll rounded-xl
bg="white dark:[#0f060c]"
bg="white dark:primary-950"
>
<ChatHistory h-full flex-1 p-4 w="full" max-h="<md:[60%]" />
<div h="<md:full" flex gap-2>
+1 -1
View File
@@ -20,7 +20,7 @@ function handleSettingsOpen(open: boolean) {
<template>
<Cross>
<AnimatedBackground :fill-color="dark ? 'oklch(95% 0.20 var(--theme-colors-hue))' : 'oklch(99% 0.10 var(--theme-colors-hue))'">
<AnimatedBackground :fill-color="dark ? 'oklch(29% var(--theme-colors-chroma) var(--theme-colors-hue))' : 'color-mix(in srgb, oklch(95% var(--theme-colors-chroma-50) var(--theme-colors-hue)) 80%, oklch(100% 0 360))'">
<div relative flex="~ col" z-2 h-100vh w-100vw of-hidden>
<!-- header -->
<div>
@@ -72,10 +72,10 @@ const settings = useSettings()
<div bg="primary-500" class="primary-color-bar" text-black>
500
</div>
<div bg="primary-600" class="primary-color-bar" text-black>
<div bg="primary-600" class="primary-color-bar" text-white>
600
</div>
<div bg="primary-700" class="primary-color-bar" text-black>
<div bg="primary-700" class="primary-color-bar" text-white>
700
</div>
<div bg="primary-800" class="primary-color-bar" text-white>
@@ -87,17 +87,41 @@ const settings = useSettings()
<div bg="primary-950" class="primary-color-bar" text-white>
950
</div>
<div bg="primary-960" class="primary-color-bar" text-white>
960
</div>
<div mt-4 h-10 w-full flex overflow-hidden rounded-lg class="transparency-grid">
<div bg="primary-500/5" class="primary-color-bar" text-black>
500/5
</div>
<div bg="primary-970" class="primary-color-bar" text-white>
970
<div bg="primary-500/10" class="primary-color-bar" text-black>
500/10
</div>
<div bg="primary-980" class="primary-color-bar" text-white>
980
<div bg="primary-500/20" class="primary-color-bar" text-black>
500/20
</div>
<div bg="primary-990" class="primary-color-bar" text-white>
990
<div bg="primary-500/30" class="primary-color-bar" text-black>
500/30
</div>
<div bg="primary-500/40" class="primary-color-bar" text-black>
500/40
</div>
<div bg="primary-500/50" class="primary-color-bar" text-black>
500/50
</div>
<div bg="primary-500/60" class="primary-color-bar" text-black>
500/60
</div>
<div bg="primary-500/70" class="primary-color-bar" text-black>
500/70
</div>
<div bg="primary-500/80" class="primary-color-bar" text-black>
500/80
</div>
<div bg="primary-500/90" class="primary-color-bar" text-black>
500/90
</div>
<div bg="primary-500" class="primary-color-bar" text-black>
500
</div>
</div>
</div>
@@ -125,6 +149,19 @@ const settings = useSettings()
oklch(85% 0.2 360)
);
}
.transparency-grid {
background-image: linear-gradient(45deg, #ccc 25%, transparent 25%),
linear-gradient(-45deg, #ccc 25%, transparent 25%), linear-gradient(45deg, transparent 75%, #ccc 75%),
linear-gradient(-45deg, transparent 75%, #ccc 75%);
background-size: 20px 20px;
background-position:
0 0,
0 10px,
10px -10px,
-10px 0px;
background-color: #fff;
}
</style>
<route lang="yaml">
+12
View File
@@ -10,5 +10,17 @@
--airi-theme-primary-800: #a70a3e;
--airi-theme-primary-900: #8e0d3b;
--airi-theme-primary-950: #50011b;
--theme-colors-hue: 354.31;
--theme-colors-chroma: calc(0.18 + (cos(var(--theme-colors-hue) * 3.14159265 / 180) * 0.04));
--theme-colors-chroma-50: calc(var(--theme-colors-chroma) * 0.3);
--theme-colors-chroma-100: calc(var(--theme-colors-chroma) * 0.5);
--theme-colors-chroma-200: calc(var(--theme-colors-chroma) * 0.6);
--theme-colors-chroma-300: calc(var(--theme-colors-chroma) * 0.75);
--theme-colors-chroma-400: var(--theme-colors-chroma);
--theme-colors-chroma-600: calc(var(--theme-colors-chroma) * 1.15);
--theme-colors-chroma-700: calc(var(--theme-colors-chroma) * 1.1);
--theme-colors-chroma-800: calc(var(--theme-colors-chroma) * 0.85);
--theme-colors-chroma-900: calc(var(--theme-colors-chroma) * 0.7);
--theme-colors-chroma-950: calc(var(--theme-colors-chroma) * 0.5);
}
+12 -16
View File
@@ -74,22 +74,18 @@ export default defineConfig({
theme: {
colors: {
primary: {
DEFAULT: 'oklch(62% 0.20 var(--theme-colors-hue))',
50: 'color-mix(in srgb, oklch(95% 0.10 var(--theme-colors-hue)) 30%, oklch(100% 0 360))',
100: 'color-mix(in srgb, oklch(95% 0.10 var(--theme-colors-hue)) 80%, oklch(100% 0 360))',
200: 'oklch(90% 0.12 var(--theme-colors-hue))',
300: 'oklch(85% 0.20 var(--theme-colors-hue))',
400: 'oklch(74% 0.20 var(--theme-colors-hue))',
500: 'oklch(62% 0.20 var(--theme-colors-hue))',
600: 'oklch(54% 0.23 var(--theme-colors-hue))',
700: 'oklch(49% 0.21 var(--theme-colors-hue))',
800: 'oklch(42% 0.17 var(--theme-colors-hue))',
900: 'oklch(37% 0.14 var(--theme-colors-hue))',
950: 'oklch(29% 0.09 var(--theme-colors-hue))',
960: 'oklch(23% 0.09 var(--theme-colors-hue))',
970: 'oklch(17% 0.09 var(--theme-colors-hue))',
980: 'oklch(11% 0.09 var(--theme-colors-hue))',
990: 'oklch(6% 0.09 var(--theme-colors-hue))',
DEFAULT: 'oklch(62% var(--theme-colors-chroma) var(--theme-colors-hue))',
50: 'color-mix(in srgb, oklch(95% var(--theme-colors-chroma-50) var(--theme-colors-hue)) 30%, oklch(100% 0 360))',
100: 'color-mix(in srgb, oklch(95% var(--theme-colors-chroma-100) var(--theme-colors-hue)) 80%, oklch(100% 0 360))',
200: 'oklch(90% var(--theme-colors-chroma-200) var(--theme-colors-hue))',
300: 'oklch(85% var(--theme-colors-chroma-300) var(--theme-colors-hue))',
400: 'oklch(74% var(--theme-colors-chroma-400) var(--theme-colors-hue))',
500: 'oklch(62% var(--theme-colors-chroma) var(--theme-colors-hue))',
600: 'oklch(54% var(--theme-colors-chroma-600) var(--theme-colors-hue))',
700: 'oklch(49% var(--theme-colors-chroma-700) var(--theme-colors-hue))',
800: 'oklch(42% var(--theme-colors-chroma-800) var(--theme-colors-hue))',
900: 'oklch(37% var(--theme-colors-chroma-900) var(--theme-colors-hue))',
950: 'oklch(29% var(--theme-colors-chroma-950) var(--theme-colors-hue))',
},
},
},
@@ -7,6 +7,7 @@ import { extensions } from '@pixi/extensions'
import { InteractionManager } from '@pixi/interaction'
import { Ticker, TickerPlugin } from '@pixi/ticker'
import { breakpointsTailwind, useBreakpoints, useDark, useDebounceFn, watchDebounced } from '@vueuse/core'
import { formatHex } from 'culori'
import localforage from 'localforage'
import { storeToRefs } from 'pinia'
import { DropShadowFilter } from 'pixi-filters'
@@ -65,6 +66,7 @@ const {
availableLive2dMotions,
live2dLoadSource,
live2dModelUrl,
themeColorsHue,
} = storeToRefs(useSettings())
const currentMotion = ref<{ group: string, index: number }>({ group: 'Idle', index: 0 })
@@ -196,11 +198,15 @@ const handleResize = useDebounceFn(() => {
}
}, 100)
const dropShadowColorComputer = ref<HTMLDivElement>()
function updateDropShadowFilter() {
if (model.value) {
const color = getComputedStyle(dropShadowColorComputer.value!).backgroundColor
const colorNumber = Number(formatHex(color)!.replace('#', '0x'))
model.value.filters = [new DropShadowFilter({
color: dark.value ? 0x99667F : 0xDFB9D2,
alpha: 0.3,
color: colorNumber,
alpha: 0.2,
blur: 0,
distance: 20,
rotation: 45,
@@ -210,7 +216,7 @@ function updateDropShadowFilter() {
watch([() => props.width, () => props.height], () => handleResize())
watch(dark, updateDropShadowFilter, { immediate: true })
watch(model, updateDropShadowFilter)
watch([model, themeColorsHue], updateDropShadowFilter)
watch(mouthOpenSize, value => getCoreModel().setParameterValueById('ParamMouthOpenY', value))
watch(pixiApp, initLive2DPixiStage)
watch(live2dCurrentMotion, value => setMotion(value.group, value.index))
@@ -232,5 +238,6 @@ onUnmounted(() => {
</script>
<template>
<div ref="dropShadowColorComputer" hidden bg="primary-400 dark:primary-500" />
<slot />
</template>
+17
View File
@@ -538,6 +538,9 @@ importers:
'@xsai/utils-chat':
specifier: 'catalog:'
version: 0.1.2
culori:
specifier: ^4.0.1
version: 4.0.1
defu:
specifier: ^6.1.4
version: 6.1.4
@@ -677,6 +680,9 @@ importers:
'@shikijs/markdown-it':
specifier: ^3.1.0
version: 3.1.0(markdown-it-async@2.0.0)
'@types/culori':
specifier: ^2.1.1
version: 2.1.1
'@types/markdown-it-link-attributes':
specifier: ^3.0.5
version: 3.0.5
@@ -4329,6 +4335,9 @@ packages:
'@types/cors@2.8.17':
resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==}
'@types/culori@2.1.1':
resolution: {integrity: sha512-NzLYD0vNHLxTdPp8+RlvGbR2NfOZkwxcYGFwxNtm+WH2NuUNV8785zv1h0sulFQ5aFQ9n/jNDUuJeo3Bh7+oFA==}
'@types/d3-array@3.2.1':
resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==}
@@ -6068,6 +6077,10 @@ packages:
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
culori@4.0.1:
resolution: {integrity: sha512-LSnjA6HuIUOlkfKVbzi2OlToZE8OjFi667JWN9qNymXVXzGDmvuP60SSgC+e92sd7B7158f7Fy3Mb6rXS5EDPw==}
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
d3-array@3.2.4:
resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
engines: {node: '>=12'}
@@ -14669,6 +14682,8 @@ snapshots:
dependencies:
'@types/node': 22.13.9
'@types/culori@2.1.1': {}
'@types/d3-array@3.2.1': {}
'@types/d3-axis@3.0.6':
@@ -17107,6 +17122,8 @@ snapshots:
csstype@3.1.3: {}
culori@4.0.1: {}
d3-array@3.2.4:
dependencies:
internmap: 2.0.3