feat. [SWV-807] 게임 폰트 기능 추가
This commit is contained in:
138
app/app.vue
138
app/app.vue
@@ -3,87 +3,129 @@ import { useNuxtApp } from 'nuxt/app'
|
||||
import type {
|
||||
GameDataMetaTag,
|
||||
GameDataValue,
|
||||
GameDataKeyColors,
|
||||
GameDataImg,
|
||||
} from '#layers/types/api/gameData'
|
||||
|
||||
const nuxtApp = useNuxtApp()
|
||||
const { locale } = useI18n()
|
||||
|
||||
const gameDataStore = useGameDataStore()
|
||||
const modalStore = useModalStore()
|
||||
const scrollStore = useScrollStore()
|
||||
|
||||
const { setGameData } = gameDataStore
|
||||
const { gameName, gaCode } = storeToRefs(gameDataStore)
|
||||
const { confirm, alert } = modalStore
|
||||
const { gameName, gaCode } = storeToRefs(gameDataStore)
|
||||
const { scrollGnbPosition } = storeToRefs(scrollStore)
|
||||
|
||||
// 통합 메타데이터 설정
|
||||
const setupAllMetaData = (data: GameDataValue) => {
|
||||
const meta = data.meta_tag_json ?? ({} as GameDataMetaTag)
|
||||
const faviconPath = data.favicon_json ?? ({} as GameDataImg)
|
||||
const theme = data.design_theme === 1 ? 'light' : 'dark'
|
||||
// favicon 링크 생성 헬퍼
|
||||
const createStyleLinks = (faviconJson: GameDataImg, fontPath: string = '') => {
|
||||
const links = []
|
||||
const iconUrl = faviconJson[0]
|
||||
const appleTouchIconUrl = faviconJson[1]
|
||||
const pngIconUrl = faviconJson[2]
|
||||
|
||||
// 파비콘 링크 생성
|
||||
const faviconLinks = [
|
||||
{
|
||||
if (iconUrl) {
|
||||
links.push({
|
||||
rel: 'icon',
|
||||
type: 'image/x-icon',
|
||||
href: formatPathHost(faviconPath[0]),
|
||||
},
|
||||
{
|
||||
href: formatPathHost(iconUrl),
|
||||
})
|
||||
}
|
||||
if (appleTouchIconUrl) {
|
||||
links.push({
|
||||
rel: 'apple-touch-icon',
|
||||
href: formatPathHost(faviconPath[1]),
|
||||
},
|
||||
{
|
||||
href: formatPathHost(appleTouchIconUrl),
|
||||
})
|
||||
}
|
||||
if (pngIconUrl) {
|
||||
links.push({
|
||||
rel: 'icon',
|
||||
type: 'image/png',
|
||||
href: formatPathHost(faviconPath[2]),
|
||||
href: formatPathHost(pngIconUrl),
|
||||
})
|
||||
}
|
||||
if (fontPath) {
|
||||
links.push({
|
||||
rel: 'stylesheet',
|
||||
href: formatPathHost(fontPath),
|
||||
})
|
||||
}
|
||||
|
||||
return links
|
||||
}
|
||||
|
||||
// 메타 태그 생성 헬퍼
|
||||
const createMetaTags = (metaTag: Partial<GameDataMetaTag> = {}) => {
|
||||
const metaList = [
|
||||
{ name: 'description', content: metaTag.page_desc },
|
||||
{ property: 'og:title', content: metaTag.og_title },
|
||||
{ property: 'og:description', content: metaTag.og_desc },
|
||||
{
|
||||
property: 'og:image',
|
||||
content: formatPathHost(metaTag.og_image),
|
||||
},
|
||||
{ name: 'twitter:title', content: metaTag.x_title },
|
||||
{ name: 'twitter:description', content: metaTag.x_desc },
|
||||
{
|
||||
name: 'twitter:image',
|
||||
content: formatPathHost(metaTag.x_image),
|
||||
},
|
||||
]
|
||||
|
||||
// 색상 CSS 변수 생성
|
||||
const cssColorVariables = Object.entries(data.key_color_json ?? {})
|
||||
// content가 유효한 메타 태그만 필터링
|
||||
return metaList.filter(
|
||||
meta => meta.content && String(meta.content).trim() !== ''
|
||||
)
|
||||
}
|
||||
|
||||
// CSS 변수 생성 헬퍼
|
||||
const createCssVariable = (keyColorJson: GameDataKeyColors) => {
|
||||
const colorVariables = Object.entries(keyColorJson)
|
||||
.filter(([key, value]) => key && value != null)
|
||||
.map(([key, value]) => `--${key}: ${value};`)
|
||||
.join('\n ')
|
||||
|
||||
const cssContent = `
|
||||
:root {
|
||||
${cssColorVariables}
|
||||
}
|
||||
`
|
||||
return `:root {${colorVariables}}`
|
||||
}
|
||||
|
||||
useHead({
|
||||
title: meta?.page_title ?? '',
|
||||
meta: [
|
||||
{ name: 'description', content: meta.page_desc },
|
||||
{ property: 'og:title', content: meta.og_title },
|
||||
{ property: 'og:description', content: meta.og_desc },
|
||||
{ property: 'og:image', content: formatPathHost(meta.og_image) },
|
||||
{ name: 'twitter:title', content: meta.x_title },
|
||||
{ name: 'twitter:description', content: meta.x_desc },
|
||||
{ name: 'twitter:image', content: formatPathHost(meta.x_image) },
|
||||
],
|
||||
htmlAttrs: {
|
||||
'data-game': data.game_name || '',
|
||||
'data-theme': theme,
|
||||
lang: locale.value ?? data.default_lang_code,
|
||||
},
|
||||
link: faviconLinks,
|
||||
style: [
|
||||
{
|
||||
innerHTML: cssContent,
|
||||
id: 'game-css-variables',
|
||||
// 게임 헤드 설정
|
||||
const setupGameHead = (data: GameDataValue) => {
|
||||
try {
|
||||
const metaTag: Partial<GameDataMetaTag> = data.meta_tag_json ?? {}
|
||||
const designTheme = data.design_theme === 1 ? 'light' : 'dark'
|
||||
const styleLinks = createStyleLinks(
|
||||
data.favicon_json
|
||||
// data?.game_font?.font_path
|
||||
)
|
||||
|
||||
useHead({
|
||||
title: metaTag.page_title ?? '',
|
||||
meta: createMetaTags(metaTag),
|
||||
htmlAttrs: {
|
||||
'data-game': data.game_name ?? '',
|
||||
'data-theme': designTheme,
|
||||
lang: locale.value ?? data.default_lang_code ?? 'ko',
|
||||
},
|
||||
],
|
||||
})
|
||||
link: styleLinks,
|
||||
style: [
|
||||
{
|
||||
innerHTML: createCssVariable(data.key_color_json),
|
||||
id: 'game-css-variables',
|
||||
},
|
||||
],
|
||||
})
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('[setupGameHead] Failed to setup game head:', error)
|
||||
}
|
||||
}
|
||||
|
||||
if (import.meta.server) {
|
||||
const gameData = nuxtApp.ssrContext?.event?.context?.gameData
|
||||
if (gameData) {
|
||||
setGameData(gameData)
|
||||
setupAllMetaData(gameData)
|
||||
setupGameHead(gameData)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,16 +12,32 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
objectFit: 'contain',
|
||||
})
|
||||
|
||||
const gameDataStore = useGameDataStore()
|
||||
const { fontFamily } = storeToRefs(gameDataStore)
|
||||
|
||||
const imagePaths = computed(() => getResourceSrc(props.resourcesData))
|
||||
const displayText = computed(() => props.resourcesData?.display?.text)
|
||||
const displayColor = computed(() =>
|
||||
getColorCodeFromData(props.resourcesData?.display, 'none')
|
||||
)
|
||||
|
||||
// HTML 콘텐츠 정리 (줄바꿈 처리)
|
||||
// HTML 콘텐츠 줄바꿈 처리
|
||||
const sanitizedContent = computed(() => {
|
||||
return displayText.value?.replace(/\n/g, '<br/>') || ''
|
||||
})
|
||||
|
||||
// 텍스트 스타일
|
||||
const textStyle = computed(() => {
|
||||
const style: Record<string, string> = {
|
||||
color: displayColor.value,
|
||||
}
|
||||
|
||||
if (props.resourcesData?.display?.use_game_font === 1) {
|
||||
style.fontFamily = fontFamily.value
|
||||
}
|
||||
|
||||
return style
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -35,7 +51,7 @@ const sanitizedContent = computed(() => {
|
||||
<span
|
||||
v-else-if="isTypeText(resourcesData?.resource_type)"
|
||||
v-dompurify-html="sanitizedContent"
|
||||
:style="{ color: displayColor }"
|
||||
:style="textStyle"
|
||||
class="block"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@@ -17,6 +17,7 @@ export const useGameDataStore = defineStore('gameData', () => {
|
||||
urlJson: null as GameDataValue['url_json'] | null,
|
||||
gnb: null as GameDataValue['gnb'] | null,
|
||||
eventBanner: null as GameDataValue['event_banner'] | null,
|
||||
fontFamily: null as GameDataValue['game_font']['font_family'] | null,
|
||||
})
|
||||
|
||||
const state = reactive(getInitialState())
|
||||
@@ -37,6 +38,7 @@ export const useGameDataStore = defineStore('gameData', () => {
|
||||
state.urlJson = data?.url_json
|
||||
state.gnb = data?.gnb
|
||||
state.eventBanner = data?.event_banner
|
||||
state.fontFamily = data?.game_font?.font_family
|
||||
}
|
||||
|
||||
const clearGameData = () => {
|
||||
|
||||
@@ -29,7 +29,6 @@ export interface GameDataValue {
|
||||
design_theme: number
|
||||
lang_codes: string[]
|
||||
key_color_json: GameDataKeyColors
|
||||
use_game_font: boolean
|
||||
comm_sns_bg_color_json: {
|
||||
display: ColorObject
|
||||
}
|
||||
@@ -73,8 +72,7 @@ export interface GameDataKeyColors {
|
||||
// 게임 폰트 타입
|
||||
export interface GameDataGameFont {
|
||||
font_family: string
|
||||
font_weight: string
|
||||
font_style: string
|
||||
font_path: string
|
||||
}
|
||||
|
||||
// 파비콘 경로 타입
|
||||
|
||||
@@ -87,11 +87,13 @@ export interface PageDataResourceGroupBtnInfo extends ColorObject {
|
||||
disabled: boolean
|
||||
txt_btn_name: string
|
||||
detail: Record<string, any>
|
||||
use_game_font: 0 | 1
|
||||
}
|
||||
|
||||
// 리소스 그룹 타입
|
||||
export interface PageDataResourceGroupDisplay extends ColorObject {
|
||||
text: string
|
||||
use_game_font: 0 | 1
|
||||
}
|
||||
|
||||
export interface PageDataResourceGroup {
|
||||
|
||||
Reference in New Issue
Block a user