fix: cape button & dynamic nametag sizing

This commit is contained in:
Calum H.
2025-05-25 20:16:01 +01:00
committed by Alejandro González
parent 0155262ddd
commit 3b9cbc6f8b
3 changed files with 96 additions and 42 deletions

View File

@@ -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"

View 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 {

View File

@@ -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);