202 lines
5.1 KiB
Vue
202 lines
5.1 KiB
Vue
<script setup lang="ts">
|
|
import { useNuxtApp } from 'nuxt/app'
|
|
import type {
|
|
GameDataFavicon,
|
|
GameDataMetaTag,
|
|
GameDataValue,
|
|
} from '#layers/types/api/gameData'
|
|
|
|
const nuxtApp = useNuxtApp()
|
|
|
|
const gameDataStore = useGameDataStore()
|
|
const modalStore = useModalStore()
|
|
const scrollStore = useScrollStore()
|
|
|
|
const { setGameData } = gameDataStore
|
|
const { gameData } = storeToRefs(gameDataStore)
|
|
const { confirm, alert } = modalStore
|
|
const { scrollGnbPosition } = storeToRefs(scrollStore)
|
|
|
|
const metaData = ref<GameDataMetaTag | null>(null)
|
|
|
|
// SSR에서 게임 데이터 가져오기
|
|
const getGameDataFromServer = (): GameDataValue | null => {
|
|
return import.meta.server
|
|
? (nuxtApp.ssrContext?.event.context.gameData ?? null)
|
|
: null
|
|
}
|
|
|
|
// 통합 메타데이터 설정
|
|
const setupAllMetaData = (data: GameDataValue) => {
|
|
const meta = data.meta_tag_json ?? ({} as GameDataMetaTag)
|
|
const faviconPath = data.favicon_json ?? ({} as GameDataFavicon)
|
|
const theme = data.design_theme === 1 ? 'light' : 'dark'
|
|
|
|
// 파비콘 링크 생성
|
|
const faviconLinks = [
|
|
{
|
|
rel: 'icon',
|
|
type: 'image/x-icon',
|
|
href: formatPathHost(faviconPath[0]),
|
|
},
|
|
{
|
|
rel: 'apple-touch-icon',
|
|
href: formatPathHost(faviconPath[1]),
|
|
},
|
|
{
|
|
rel: 'icon',
|
|
type: 'image/png',
|
|
href: formatPathHost(faviconPath[2]),
|
|
},
|
|
]
|
|
|
|
// 색상 CSS 변수 생성
|
|
const cssColorVariables = Object.entries(data.key_color_json ?? {})
|
|
.map(([key, value]) => `--${key}: ${value};`)
|
|
.join('\n ')
|
|
|
|
const commImgVariables =
|
|
data.comm_img_json?.groups
|
|
?.map(
|
|
({ img_name, img_path }) =>
|
|
`--${img_name}: url(${formatPathHost(img_path?.comm ?? '')});`
|
|
)
|
|
.join('\n ') ?? ''
|
|
|
|
const cssContent = `
|
|
:root {
|
|
${cssColorVariables}
|
|
${commImgVariables}
|
|
}
|
|
`
|
|
|
|
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: data.default_lang_code || 'ko',
|
|
},
|
|
link: faviconLinks,
|
|
style: [
|
|
{
|
|
innerHTML: cssContent,
|
|
id: 'game-css-variables',
|
|
},
|
|
],
|
|
})
|
|
}
|
|
|
|
// 메타 데이터 설정
|
|
const setupMetaData = (data: GameDataValue) => {
|
|
metaData.value = data.meta_tag_json
|
|
setupAllMetaData(data)
|
|
}
|
|
|
|
// 초기화 로직 실행
|
|
const serverGameData = getGameDataFromServer()
|
|
|
|
if (serverGameData) {
|
|
setGameData(serverGameData)
|
|
setupMetaData(serverGameData)
|
|
}
|
|
|
|
// Google Analytics 설정 (클라이언트에서만 실행)
|
|
if (import.meta.client) {
|
|
const { gtag, initialize } = useGtag()
|
|
initialize(gameData.value?.ga_code)
|
|
gtag('event', 'screen_view', {
|
|
app_name: 'My App',
|
|
screen_name: 'Home',
|
|
})
|
|
}
|
|
|
|
let rafId: number | null = null
|
|
let stopWatch: (() => void) | null = null
|
|
|
|
onMounted(() => {
|
|
if (!import.meta.client) return
|
|
|
|
useEventListener('scroll', scrollStore.updateScrollValue, { passive: true })
|
|
|
|
stopWatch = watch(
|
|
scrollGnbPosition,
|
|
newValue => {
|
|
if (rafId) {
|
|
cancelAnimationFrame(rafId)
|
|
}
|
|
rafId = requestAnimationFrame(() => {
|
|
document.documentElement.style.setProperty(
|
|
'--scroll-position',
|
|
`${newValue}px`
|
|
)
|
|
rafId = null
|
|
})
|
|
},
|
|
{ immediate: true }
|
|
)
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
// watch 정리
|
|
if (stopWatch) {
|
|
stopWatch()
|
|
stopWatch = null
|
|
}
|
|
|
|
// requestAnimationFrame 정리
|
|
if (rafId) {
|
|
cancelAnimationFrame(rafId)
|
|
rafId = null
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<h1 class="sr-only">{{ gameData?.game_name }}</h1>
|
|
<NuxtPage />
|
|
|
|
<!-- 공통 모달 컴포넌트 -->
|
|
<WidgetsModalClient />
|
|
<BlocksModalYouTube />
|
|
<BlocksModalContent />
|
|
<BlocksModalConfirm
|
|
v-model:is-open="confirm.storeIsOpen"
|
|
:is-show-dimmed="confirm.storeIsShowDimmed"
|
|
:content-text="confirm.storeContentText"
|
|
:confirm-button-text="confirm.storeConfirmButtonText"
|
|
:cancel-button-text="confirm.storeCancelButtonText"
|
|
:is-outside-close="confirm.storeIsOutsideClose"
|
|
:modal-name="confirm.storeModalName"
|
|
@confirm-button-event="confirm.storeConfirmButtonEvent"
|
|
@cancel-button-event="confirm.storeCancelButtonEvent"
|
|
/>
|
|
<BlocksModalAlert
|
|
v-model:is-open="alert.storeIsOpen"
|
|
:is-show-dimmed="alert.storeIsShowDimmed"
|
|
:content-text="alert.storeContentText"
|
|
:confirm-button-text="alert.storeConfirmButtonText"
|
|
:is-outside-close="alert.storeIsOutsideClose"
|
|
:modal-name="alert.storeModalName"
|
|
@confirm-button-event="alert.storeConfirmButtonEvent"
|
|
/>
|
|
<BlocksModalToast />
|
|
|
|
<!-- 로딩 컴포넌트 -->
|
|
<AtomsLoadingFull />
|
|
<AtomsLoadingLocal />
|
|
</template>
|
|
|
|
<style>
|
|
@import '#layers/assets/css/app.css';
|
|
</style>
|