feat.게임 공통 이미지 적용 (video, arrow)

This commit is contained in:
clkim
2025-10-16 14:18:12 +09:00
parent f60cb3d6c5
commit 6dff3787b6
11 changed files with 89 additions and 61 deletions

View File

@@ -1,6 +1,7 @@
<script setup lang="ts">
import { useNuxtApp } from 'nuxt/app'
import type { GameDataMetaTag, GameDataValue } from '#layers/types/api/gameData'
import { getResolvedHost } from '#layers/utils/styleUtil'
const nuxtApp = useNuxtApp()
@@ -120,6 +121,14 @@ gtag('event', 'screen_view', {
onMounted(() => {
useEventListener('scroll', scrollStore.updateScrollValue)
if (gameData.value?.comm_img) {
gameData.value?.comm_img.groups.forEach(group => {
const cssVarName = `--${group.img_name}`
const imageUrl = `url(${getResolvedHost(group.img_path.comm)})`
document.documentElement.style.setProperty(cssVarName, imageUrl)
})
}
})
onBeforeUnmount(() => {

View File

@@ -1,34 +1,18 @@
/* Button Size Classes */
@layer components {
.btn-base {
@apply overflow-hidden relative inline-flex items-center justify-center font-medium cursor-pointer
before:content-[''] before:absolute before:top-0 before:left-0 before:w-full before:h-full before:border before:border-white/10 before:rounded-lg
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:rounded-lg after:transition-opacity after:duration-300 after:ease-in-out after:opacity-0
/* 기본 크기: size-medium */
px-10 h-14 text-base rounded-lg;
}
.btn-base:hover {
@apply after:opacity-20;
}
.btn-base:disabled {
@apply cursor-default
after:bg-[var(--text-color)] after:opacity-20 after:z-[2];
}
.btn-base .btn-content {
@apply relative flex items-center gap-1 z-[1];
}
.size-large {
@apply px-10 h-16 text-lg;
@apply px-10 h-16 text-lg rounded-lg
before:rounded after:rounded;
}
.size-medium {
@apply px-10 h-14 text-base;
@apply px-10 h-14 text-base rounded-lg
before:rounded after:rounded;
}
.size-small {
@apply px-10 h-12 text-sm;
@apply px-10 h-12 text-sm rounded-lg
before:rounded after:rounded;
}
.size-extra-small {

View File

@@ -22,10 +22,10 @@
hover:after:opacity-10;
}
.arrow-prev {
@apply bg-[image:var(--arrow-left)];
@apply bg-[image:var(--arrow-prev)];
}
.arrow-next {
@apply bg-[image:var(--arrow-right)];
@apply bg-[image:var(--arrow-next)];
}
.type-full .arrow-prev {

View File

@@ -1,28 +1,19 @@
<script setup lang="ts">
import type { PageDataResourceGroup } from '#layers/types/api/pageData'
const props = defineProps<{ resourcesData: PageDataResourceGroup }>()
const emit = defineEmits<{
(e: 'click'): void
}>()
const bgStyles = getResponsiveSrc(props.resourcesData?.res_path, {
resourcesType: 'bg',
})
</script>
<template>
<button
v-if="resourcesData && bgStyles"
class="relative group bg-cover bg-center bg-no-repeat w-[66px] h-[66px] md:w-[100px] md:h-[100px]"
:class="getResponsiveClass()"
:style="bgStyles"
@click="emit('click')"
>
<span
class="absolute inset-0 m-[10px] bg-white opacity-0 group-hover:opacity-10 transition-opacity duration-300 ease-in-out rounded-[50%]"
/>
<button class="btn-play" @click="emit('click')">
<span class="sr-only">Play</span>
</button>
</template>
<style scoped>
.btn-play {
@apply relative w-[66px] h-[66px] bg-[image:var(--video-play)] bg-cover bg-center bg-no-repeat md:w-[100px] md:h-[100px]
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:rounded-[50%] after:opacity-0 after:transition-opacity after:duration-300 after:ease-in-out
hover:after:opacity-10;
}
</style>

View File

@@ -13,29 +13,29 @@ const props = withDefaults(defineProps<props>(), {
</script>
<template>
<button :class="['btn-modal', props.variant]" :disabled="props.disabled">
<button :class="['btn-base', props.variant]" :disabled="props.disabled">
<slot />
</button>
</template>
<style scoped>
.btn-modal {
.btn-base {
@apply relative w-full py-[14px] px-5 text-sm font-medium rounded-lg
before:content-[''] before:absolute before:top-0 before:left-0 before:w-full before:h-full before:border before:border-black/10 before:rounded-lg before:transition-all before:duration-300 before:ease-in-out;
}
.btn-modal.filled {
.btn-base.filled {
@apply bg-[var(--primary)] text-[var(--text-secondary)]
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:rounded-lg after:transition-opacity after:duration-300 after:ease-in-out after:opacity-0
hover:after:opacity-20;
}
.btn-modal.outlined {
.btn-base.outlined {
@apply bg-white text-[#333333]
hover:before:border-[#999];
}
.btn-modal:disabled {
.btn-base:disabled {
@apply cursor-default bg-[#EBEBEB] text-[#999]
before:border-[#D9D9D9]
after:hidden

View File

@@ -3,6 +3,7 @@ import type { ButtonType } from '#layers/types/components/button'
interface props {
type?: ButtonType
buttonSize?: string
target?: '_self' | '_blank'
href?: string
rel?: string
@@ -14,6 +15,7 @@ interface props {
const props = withDefaults(defineProps<props>(), {
type: 'action',
buttonSize: 'size-extra-small md:size-medium',
backgroundColor: 'var(--primary)',
textColor: 'var(--alternative-02)',
disabled: false,
@@ -72,7 +74,7 @@ const buttonStyles = computed(() => {
<component
:is="componentTag"
v-bind="{ ...componentProps, ...$attrs }"
:class="['btn-base', $attrs?.class]"
:class="['btn-base', props.buttonSize, $attrs?.class]"
:style="buttonStyles"
>
<span class="btn-content">
@@ -87,3 +89,22 @@ const buttonStyles = computed(() => {
</span>
</component>
</template>
<style scoped>
.btn-base {
@apply overflow-hidden relative inline-flex items-center justify-center font-medium cursor-pointer
before:content-[''] before:absolute before:top-0 before:left-0 before:w-full before:h-full before:border before:border-white/10
after:content-[''] after:absolute after:top-0 after:left-0 after:w-full after:h-full after:bg-white after:transition-opacity after:duration-300 after:ease-in-out after:opacity-0;
}
.btn-base:hover {
@apply after:opacity-20;
}
.btn-base:disabled {
@apply cursor-default
after:bg-[var(--text-color)] after:opacity-20 after:z-[2];
}
.btn-base .btn-content {
@apply relative flex items-center gap-1 z-[1];
}
</style>

View File

@@ -1,10 +1,8 @@
<template>
<div id="stove-wrapper" class="relative z-[5]" />
</template>
<script setup lang="ts">
import { useGameDataStore } from '#layers/stores/useGameDataStore'
let cpHeader: any = null
const runtimeConfig = useRuntimeConfig()
const { locale, availableLocales } = useI18n()
const { gameData } = useGameDataStore()
@@ -22,7 +20,7 @@ const languageCodes = computed(() => {
return [locale]
})
function loadGnb(locale: string) {
const loadGnb = (locale: string) => {
locale = locale.toLowerCase()
const gnbOption = {
@@ -51,11 +49,22 @@ function loadGnb(locale: string) {
},
}
const cpHeader = new (window as any).cp.Header(gnbOption)
cpHeader = new (window as any).cp.Header(gnbOption)
cpHeader.render()
}
onMounted(() => {
loadGnb(locale.value)
})
onBeforeUnmount(() => {
if (cpHeader && typeof cpHeader.destroy === 'function') {
cpHeader.destroy()
}
cpHeader = null
})
</script>
<template>
<div id="stove-wrapper" class="relative z-[5]" />
</template>

View File

@@ -46,11 +46,11 @@ const handleOutsideClick = () => {
></p>
<slot></slot>
<div class="content-btns">
<AtomsButtonModal
<AtomsButtonVariant
@click="setButtonEvent(() => emit('confirmButtonEvent'))"
>
{{ props.confirmButtonText || '확인' }}
</AtomsButtonModal>
</AtomsButtonVariant>
</div>
</div>
</div>

View File

@@ -47,17 +47,17 @@ const handleOutsideClick = () => {
></p>
<slot></slot>
<div class="content-btns">
<AtomsButtonModal
<AtomsButtonVariant
variant="outlined"
@click="setButtonEvent(() => emit('cancelButtonEvent'))"
>
{{ props.cancelButtonText || '취소' }}
</AtomsButtonModal>
<AtomsButtonModal
</AtomsButtonVariant>
<AtomsButtonVariant
@click="setButtonEvent(() => emit('confirmButtonEvent'))"
>
{{ props.confirmButtonText || '확인' }}
</AtomsButtonModal>
</AtomsButtonVariant>
</div>
</div>
</div>

View File

@@ -61,7 +61,6 @@ const getButtonType = (btnInfo: PageDataResourceGroupBtnInfo): ButtonType => {
})
"
:disabled="button.btn_info?.disabled"
class="size-extra-small md:size-medium"
>
{{ button.btn_info?.txt_btn_name }}
</AtomsButton>

View File

@@ -39,6 +39,7 @@ export interface GameDataValue {
meta_tag: GameDataMetaTag
sns: GameDataSns
footer: string // JSON 문자열로 변경
comm_img: GameDataCommImg
}
// ===== 세부 데이터 타입들 =====
@@ -97,6 +98,20 @@ export interface GameDataSns {
tiktok: GameDataSnsItem
}
// 공통 이미지 그룹 타입
export interface GameDataCommImgGroup {
img_name: string
img_path: {
comm: string
}
group_label: string
}
// 공통 이미지 타입
export interface GameDataCommImg {
groups: GameDataCommImgGroup[]
}
// Global 설정 타입
export interface GameDataGlobal {
system_font: string // JSON 문자열로 변경