feat. 게임 컬러 루트에 동적으로 적용

This commit is contained in:
clkim
2025-09-17 15:45:02 +09:00
parent 4f3ac0e84a
commit 2196cf4200
6 changed files with 22 additions and 184 deletions

View File

@@ -13,12 +13,14 @@ import { useNuxtApp } from 'nuxt/app'
import LoadingFull from '#layers/components/blocks/loading/Full.vue'
import LoadingLocal from '#layers/components/blocks/loading/Local.vue'
import { useGameDataStore } from '#layers/stores/useGameDataStore'
import { useGameColors } from '#layers/composables/useGameColors'
import type { GameDataMetaTag, GameDataValue } from '#layers/types/api/gameData'
const nuxtApp = useNuxtApp()
const getGameData = ref<GameDataValue | null>(null)
const metaData = ref<GameDataMetaTag | null>(null)
const { setGameData } = useGameDataStore()
const { applyGameColors } = useGameColors()
// SSR에서만 접근 가능
const gameDataFromServer = import.meta.server
@@ -30,6 +32,14 @@ if (gameDataFromServer) {
setGameData(gameDataFromServer)
}
if (import.meta.client) {
const gameDataStore = useGameDataStore()
if (gameDataStore.gameData?.key_color_codes) {
applyGameColors(gameDataStore.gameData.key_color_codes)
}
}
const meta = gameDataFromServer?.meta_tag ?? null
const theme = gameDataFromServer?.design_theme === 1 ? 'dark' : 'light'

View File

@@ -8,13 +8,6 @@
--foreground-reversal-30: #ebebeb; /* gray-80 */
--foreground-reversal-40: rgba(0, 0, 0, 0.4);
--foreground-reversal-70: #666666; /* gray-700 */
/* 게임별 동적 색상 기본값 */
--game-primary: transparent;
--game-alternative-01: transparent;
--game-alternative-02: transparent;
--game-text-primary: transparent;
--game-text-secondary: transparent;
}
/* 다크 테마 색상 */
@@ -27,13 +20,6 @@
--foreground-reversal-30: #404040; /* gray-750 */
--foreground-reversal-40: rgba(255, 255, 255, 0.4);
--foreground-reversal-70: #b2b2b2; /* gray-300 */
/* 게임별 동적 색상 기본값 */
--game-primary: transparent;
--game-alternative-01: transparent;
--game-alternative-02: transparent;
--game-text-primary: transparent;
--game-text-secondary: transparent;
}
/* 커스텀 컴포넌트 스타일 */

View File

@@ -8,8 +8,8 @@ import type {
// Props 정의
const props = withDefaults(defineProps<ButtonProps>(), {
size: 'medium',
backgroundColor: 'var(--game-primary)',
textColor: 'var(--game-text-primary)',
backgroundColor: 'var(--primary)',
textColor: 'var(--text-primary)',
icon: '',
disabled: false,
})

View File

@@ -1,160 +1,26 @@
import { useGameDataStore } from '#layers/stores/useGameDataStore'
import type { ParsedKeyColorCodes } from '#layers/types/api/gameData'
/**
* 게임별 색상 코드를 CSS 변수로 설정하는 컴포저블
* 게임 데이터의 key_color_codes를 CSS 커스텀 프로퍼티로 적용하는 기능을 제공합니다.
*/
export const useGameColors = () => {
const gameDataStore = useGameDataStore()
/**
* CSS에 정의된 기본 색상으로 리셋
* @param keyColorCodes
*/
const resetToDefaultColors = () => {
if (typeof document !== 'undefined') {
const root = document.documentElement
// CSS에 정의된 기본값으로 리셋 (브라우저가 자동으로 CSS 값을 사용하도록 함)
root.style.removeProperty('--game-primary')
root.style.removeProperty('--game-alternative-01')
root.style.removeProperty('--game-alternative-02')
root.style.removeProperty('--game-text-primary')
root.style.removeProperty('--game-text-secondary')
console.log('게임 색상을 CSS 기본값으로 리셋했습니다.')
}
}
/**
* 게임 색상 코드를 파싱하여 CSS 변수로 설정
*/
const setGameColors = () => {
if (!gameDataStore.gameData?.key_color_codes) {
console.warn(
'게임 색상 데이터가 없습니다. CSS에 정의된 기본 색상을 사용합니다.'
)
// CSS에 정의된 기본값을 명시적으로 설정 (혹시 모를 경우를 대비)
resetToDefaultColors()
const applyGameColors = (keyColorCodes: ParsedKeyColorCodes | null) => {
if (!keyColorCodes || import.meta.server) {
return
}
try {
// key_color_codes가 문자열인 경우 파싱
const colorCodes =
typeof gameDataStore.gameData.key_color_codes === 'string'
? JSON.parse(gameDataStore.gameData.key_color_codes)
: gameDataStore.gameData.key_color_codes
const root = document.documentElement
const colors = colorCodes as ParsedKeyColorCodes
// CSS 변수 설정
if (typeof document !== 'undefined') {
const root = document.documentElement
// 색상 유효성 검사 함수
const isValidColor = (color: string): boolean => {
return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(color)
}
// 기본 색상 설정 (유효성 검사 포함)
if (colors.primary && isValidColor(colors.primary)) {
root.style.setProperty('--game-primary', colors.primary)
console.log('게임 Primary 색상 설정:', colors.primary)
}
if (colors.secondary && isValidColor(colors.secondary)) {
root.style.setProperty('--game-secondary', colors.secondary)
console.log('게임 Secondary 색상 설정:', colors.secondary)
}
if (colors['text-primary'] && isValidColor(colors['text-primary'])) {
root.style.setProperty('--game-text-primary', colors['text-primary'])
console.log('게임 텍스트 Primary 색상 설정:', colors['text-primary'])
}
if (
colors['text-secondary'] &&
isValidColor(colors['text-secondary'])
) {
root.style.setProperty(
'--game-text-secondary',
colors['text-secondary']
)
console.log(
'게임 텍스트 Secondary 색상 설정:',
colors['text-secondary']
)
}
// alternative 색상들 설정
if (
colors['alternative-01'] &&
isValidColor(colors['alternative-01'])
) {
root.style.setProperty(
'--game-alternative-01',
colors['alternative-01']
)
console.log(
'게임 Alternative-01 색상 설정:',
colors['alternative-01']
)
}
if (
colors['alternative-02'] &&
isValidColor(colors['alternative-02'])
) {
root.style.setProperty(
'--game-alternative-02',
colors['alternative-02']
)
console.log(
'게임 Alternative-02 색상 설정:',
colors['alternative-02']
)
}
}
} catch (error) {
console.error('게임 색상 코드 파싱 오류:', error)
console.warn('CSS에 정의된 기본 색상을 사용합니다.')
resetToDefaultColors()
}
}
/**
* 게임 색상 코드를 반환
*/
const getGameColors = (): ParsedKeyColorCodes | null => {
if (!gameDataStore.gameData?.key_color_codes) {
return null
}
try {
const colorCodes =
typeof gameDataStore.gameData.key_color_codes === 'string'
? JSON.parse(gameDataStore.gameData.key_color_codes)
: gameDataStore.gameData.key_color_codes
return colorCodes as ParsedKeyColorCodes
} catch (error) {
console.error('게임 색상 코드 파싱 오류:', error)
return null
}
}
/**
* 특정 색상 코드를 반환
*/
const getColor = (colorKey: keyof ParsedKeyColorCodes): string | null => {
const colors = getGameColors()
return colors?.[colorKey] || null
Object.entries(keyColorCodes).forEach(([key, value]) => {
const cssVarName = `--${key}`
root.style.setProperty(cssVarName, String(value))
})
}
return {
setGameColors,
getGameColors,
getColor,
resetToDefaultColors,
applyGameColors,
}
}

View File

@@ -1,17 +0,0 @@
export default defineNuxtPlugin(() => {
const { setGameColors } = useGameColors()
// 게임 데이터가 로드된 후 색상 설정
const gameDataStore = useGameDataStore()
// 게임 데이터가 변경될 때마다 색상 업데이트
watch(
() => gameDataStore.gameData,
newGameData => {
if (newGameData) {
setGameColors()
}
},
{ immediate: true }
)
})

View File

@@ -20,13 +20,6 @@ export default {
'theme-foreground-reversal-30': 'var(--foreground-reversal-30)',
'theme-foreground-reversal-40': 'var(--foreground-reversal-40)',
'theme-foreground-reversal-70': 'var(--foreground-reversal-70)',
// 게임별 동적 색상 (CSS 변수로 설정)
'game-primary': 'var(--game-primary)',
'game-alternative-01': 'var(--game-alternative-01)',
'game-alternative-02': 'var(--game-alternative-02)',
'game-text-primary': 'var(--game-text-primary)',
'game-text-secondary': 'var(--game-text-secondary)',
},
},
},