feat.게임 공통 이미지 적용 (video, arrow)
This commit is contained in:
@@ -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(() => {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 문자열로 변경
|
||||
|
||||
Reference in New Issue
Block a user