fix: cape button & dynamic nametag sizing
This commit is contained in:
committed by
Alejandro González
parent
0155262ddd
commit
3b9cbc6f8b
@@ -16,9 +16,6 @@
|
||||
<p class="mx-auto mt-0 text-secondary text-sm text-center">
|
||||
Drag and drop or click here to browse
|
||||
</p>
|
||||
<p class="mx-auto mt-0 text-secondary text-xs text-center">
|
||||
Only 64x64 PNG files are accepted
|
||||
</p>
|
||||
<input
|
||||
ref="fileInput"
|
||||
type="file"
|
||||
|
||||
@@ -246,32 +246,7 @@ await Promise.all([loadCapes(), loadSkins(), loadCurrentUser()])
|
||||
|
||||
<div v-if="currentUser" class="p-4 skin-layout">
|
||||
<div class="preview-panel">
|
||||
<div class="flex justify-between gap-4">
|
||||
<h1 class="m-0 text-2xl font-bold">Skins</h1>
|
||||
<ButtonStyled :disabled="!!selectedSkin?.cape_id">
|
||||
<button
|
||||
v-tooltip="
|
||||
selectedSkin?.cape_id
|
||||
? 'The equipped skin is overriding the default cape.'
|
||||
: undefined
|
||||
"
|
||||
:disabled="!!selectedSkin?.cape_id"
|
||||
@click="
|
||||
(e: MouseEvent) =>
|
||||
selectCapeModal?.show(
|
||||
e,
|
||||
selectedSkin?.texture_key,
|
||||
currentCape,
|
||||
skinTexture,
|
||||
skinVariant,
|
||||
)
|
||||
"
|
||||
>
|
||||
<UpdatedIcon />
|
||||
Change cape
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</div>
|
||||
<h1 class="m-0 text-2xl font-bold">Skins</h1>
|
||||
<div class="preview-container">
|
||||
<SkinPreviewRenderer
|
||||
wide-model-src="/src/assets/models/classic_player.gltf"
|
||||
@@ -282,7 +257,33 @@ await Promise.all([loadCapes(), loadSkins(), loadCurrentUser()])
|
||||
:variant="skinVariant"
|
||||
:nametag="skinNametag"
|
||||
:initial-rotation="Math.PI / 8"
|
||||
/>
|
||||
>
|
||||
<template #subtitle>
|
||||
<ButtonStyled :disabled="!!selectedSkin?.cape_id">
|
||||
<button
|
||||
v-tooltip="
|
||||
selectedSkin?.cape_id
|
||||
? 'The equipped skin is overriding the default cape.'
|
||||
: undefined
|
||||
"
|
||||
:disabled="!!selectedSkin?.cape_id"
|
||||
@click="
|
||||
(e: MouseEvent) =>
|
||||
selectCapeModal?.show(
|
||||
e,
|
||||
selectedSkin?.texture_key,
|
||||
currentCape,
|
||||
skinTexture,
|
||||
skinVariant,
|
||||
)
|
||||
"
|
||||
>
|
||||
<UpdatedIcon />
|
||||
Change cape
|
||||
</button>
|
||||
</ButtonStyled>
|
||||
</template>
|
||||
</SkinPreviewRenderer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -404,14 +405,9 @@ $skin-card-gap: 4px;
|
||||
}
|
||||
|
||||
.preview-panel {
|
||||
position: sticky;
|
||||
top: 1.5rem;
|
||||
align-self: start;
|
||||
padding: 0.5rem;
|
||||
|
||||
@media (max-width: 700px) {
|
||||
position: static;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
|
||||
@@ -1,15 +1,22 @@
|
||||
<template>
|
||||
<div class="relative w-full h-full cursor-grab">
|
||||
<div
|
||||
class="absolute bottom-[18%] left-0 right-0 flex justify-center items-center mb-2 pointer-events-none z-10"
|
||||
class="absolute bottom-[18%] left-0 right-0 flex flex-col justify-center items-center mb-2 pointer-events-none z-10 gap-2"
|
||||
>
|
||||
<span class="text-primary text-xs px-2 py-1 rounded-full backdrop-blur-sm">
|
||||
Drag to rotate
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="absolute bottom-[10%] left-0 right-0 flex justify-center items-center pointer-events-auto z-10"
|
||||
>
|
||||
<slot name="subtitle" />
|
||||
</div>
|
||||
<div
|
||||
v-if="nametag"
|
||||
class="absolute top-[13%] left-1/2 transform -translate-x-1/2 px-3 py-1 rounded-md text-[200%] pointer-events-none z-10 font-minecraft text-inverted nametag-bg"
|
||||
ref="nametagElement"
|
||||
class="absolute top-[13%] left-1/2 transform -translate-x-1/2 px-3 py-1 rounded-md pointer-events-none z-10 font-minecraft text-inverted nametag-bg"
|
||||
:style="{ fontSize: dynamicNametagFontSize }"
|
||||
>
|
||||
{{ nametag }}
|
||||
</div>
|
||||
@@ -88,7 +95,19 @@
|
||||
import * as THREE from 'three'
|
||||
import { useGLTF } from '@tresjs/cientos'
|
||||
import { useTexture, TresCanvas, useRenderLoop } from '@tresjs/core'
|
||||
import { shallowRef, ref, computed, watch, markRaw, onBeforeMount, onUnmounted, toRefs } from 'vue'
|
||||
import {
|
||||
shallowRef,
|
||||
ref,
|
||||
computed,
|
||||
watch,
|
||||
markRaw,
|
||||
onBeforeMount,
|
||||
onUnmounted,
|
||||
toRefs,
|
||||
useTemplateRef,
|
||||
onMounted,
|
||||
nextTick,
|
||||
} from 'vue'
|
||||
import {
|
||||
applyTexture,
|
||||
applyCapeTexture,
|
||||
@@ -129,12 +148,12 @@ const props = withDefaults(
|
||||
capeSrc: undefined,
|
||||
initialRotation: 15.75,
|
||||
nametag: undefined,
|
||||
animationConfig: {
|
||||
animationConfig: () => ({
|
||||
baseAnimation: 'idle',
|
||||
randomAnimations: [],
|
||||
randomAnimationInterval: 8000,
|
||||
transitionDuration: 0.5,
|
||||
}
|
||||
}),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -161,6 +180,30 @@ const clock = new THREE.Clock()
|
||||
const currentAnimation = ref<string>('')
|
||||
const randomAnimationTimer = ref<number | null>(null)
|
||||
|
||||
const nametagElement = useTemplateRef<HTMLElement>('nametagElement')
|
||||
const maxContainerWidth = ref(300)
|
||||
|
||||
const dynamicNametagFontSize = computed(() => {
|
||||
if (!props.nametag) return '2rem'
|
||||
const textLength = props.nametag.length
|
||||
const baseSize = 32
|
||||
const minSize = 12
|
||||
const maxSize = 32
|
||||
const availableWidth = maxContainerWidth.value - 64
|
||||
const estimatedCharWidth = baseSize * 0.6
|
||||
const estimatedTextWidth = textLength * estimatedCharWidth
|
||||
const scaleFactor = availableWidth / estimatedTextWidth
|
||||
const calculatedSize = Math.max(minSize, Math.min(maxSize, baseSize * scaleFactor))
|
||||
|
||||
return `${calculatedSize}px`
|
||||
})
|
||||
|
||||
const updateContainerWidth = () => {
|
||||
if (nametagElement.value?.parentElement) {
|
||||
maxContainerWidth.value = nametagElement.value.parentElement.clientWidth
|
||||
}
|
||||
}
|
||||
|
||||
const { baseAnimation, randomAnimations } = toRefs(props.animationConfig)
|
||||
|
||||
function initializeAnimations(loadedScene: THREE.Object3D, clips: THREE.AnimationClip[]) {
|
||||
@@ -480,9 +523,25 @@ watch(
|
||||
setupRandomAnimationLoop()
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
() => props.nametag,
|
||||
() => {
|
||||
nextTick(() => {
|
||||
updateContainerWidth()
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
updateContainerWidth()
|
||||
window.addEventListener('resize', updateContainerWidth)
|
||||
})
|
||||
})
|
||||
|
||||
onBeforeMount(async () => {
|
||||
try {
|
||||
isTextureLoaded.value = false
|
||||
@@ -512,13 +571,15 @@ onUnmounted(() => {
|
||||
mixer.value.stopAllAction()
|
||||
mixer.value = null
|
||||
}
|
||||
|
||||
window.removeEventListener('resize', updateContainerWidth)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.nametag-bg {
|
||||
background: linear-gradient(308.68deg, rgba(0, 0, 0, 0) -52.46%, rgba(100, 100, 100, 0.1) 94.75%),
|
||||
rgba(0, 0, 0, 0.2);
|
||||
rgba(0, 0, 0, 0.2);
|
||||
box-shadow:
|
||||
inset -0.5px -0.5px 0px rgba(0, 0, 0, 0.25),
|
||||
inset 0.5px 0.5px 0px rgba(255, 255, 255, 0.05);
|
||||
|
||||
Reference in New Issue
Block a user