All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 55s
- Eliminar SVG complejo y usar CSS radial-gradient - Implementar degradado radial que simula iluminación real - Agregar efectos glossy con pseudo-elementos ::before y ::after - Usar box-shadow para profundidad 3D - Simplificar implementación para mejor rendimiento
183 lines
3.9 KiB
Vue
183 lines
3.9 KiB
Vue
<template>
|
|
<div class="msn-avatar-wrapper" :style="cssVars">
|
|
<div class="avatar-frame">
|
|
<div class="avatar-container">
|
|
<img
|
|
v-if="src && !imageError"
|
|
:src="src"
|
|
:alt="alt"
|
|
class="avatar-image"
|
|
@error="handleImageError"
|
|
/>
|
|
<div v-else class="avatar-placeholder">
|
|
{{ initials }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { PresenceStatus } from '~/composables/usePresence'
|
|
|
|
interface Props {
|
|
src?: string
|
|
alt?: string
|
|
presenceStatus?: PresenceStatus
|
|
size?: number
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
presenceStatus: 'online',
|
|
size: 128,
|
|
alt: 'Avatar'
|
|
})
|
|
|
|
const imageError = ref(false)
|
|
|
|
// Calcular iniciales del nombre
|
|
const initials = computed(() => {
|
|
if (!props.alt) return '?'
|
|
const words = props.alt.split(' ').filter(w => w.length > 0)
|
|
if (words.length >= 2) {
|
|
return (words[0]![0]! + words[1]![0]!).toUpperCase()
|
|
}
|
|
return props.alt.slice(0, 2).toUpperCase()
|
|
})
|
|
|
|
const handleImageError = () => {
|
|
imageError.value = true
|
|
}
|
|
|
|
// Computed para colores basados en el status prop
|
|
const colors = computed(() => {
|
|
const { PRESENCE_COLORS } = usePresence()
|
|
return PRESENCE_COLORS[props.presenceStatus]
|
|
})
|
|
|
|
// CSS Variables para pasar los colores dinámicos
|
|
const cssVars = computed(() => ({
|
|
'--color-light': colors.value.light,
|
|
'--color-medium': colors.value.medium,
|
|
'--color-dark': colors.value.dark,
|
|
'--avatar-size': `${props.size}px`
|
|
}))
|
|
</script>
|
|
|
|
<style scoped>
|
|
.msn-avatar-wrapper {
|
|
position: relative;
|
|
display: inline-block;
|
|
width: var(--avatar-size, 128px);
|
|
height: var(--avatar-size, 128px);
|
|
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
}
|
|
|
|
.msn-avatar-wrapper:hover {
|
|
transform: scale(1.05);
|
|
}
|
|
|
|
.avatar-frame {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100%;
|
|
border-radius: 12px;
|
|
padding: 8px;
|
|
|
|
/* Degradado radial para el borde - simula iluminación */
|
|
background: radial-gradient(
|
|
circle at 20% 20%,
|
|
var(--color-light) 0%,
|
|
var(--color-medium) 50%,
|
|
var(--color-dark) 100%
|
|
);
|
|
|
|
/* Sombra exterior para profundidad */
|
|
box-shadow:
|
|
0 4px 12px rgba(0, 0, 0, 0.15),
|
|
0 2px 6px rgba(0, 0, 0, 0.1),
|
|
inset 0 1px 0 rgba(255, 255, 255, 0.3);
|
|
}
|
|
|
|
/* Efecto glossy superior con pseudo-elemento */
|
|
.avatar-frame::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
right: 0;
|
|
height: 50%;
|
|
border-radius: 12px 12px 0 0;
|
|
background: linear-gradient(
|
|
180deg,
|
|
rgba(255, 255, 255, 0.4) 0%,
|
|
rgba(255, 255, 255, 0.1) 50%,
|
|
rgba(255, 255, 255, 0) 100%
|
|
);
|
|
pointer-events: none;
|
|
z-index: 2;
|
|
}
|
|
|
|
/* Highlight especular en esquina superior izquierda */
|
|
.avatar-frame::after {
|
|
content: '';
|
|
position: absolute;
|
|
top: 6px;
|
|
left: 6px;
|
|
width: 30%;
|
|
height: 30%;
|
|
border-radius: 50%;
|
|
background: radial-gradient(
|
|
circle at 30% 30%,
|
|
rgba(255, 255, 255, 0.6) 0%,
|
|
rgba(255, 255, 255, 0.2) 50%,
|
|
transparent 100%
|
|
);
|
|
pointer-events: none;
|
|
z-index: 2;
|
|
}
|
|
|
|
.avatar-container {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 100%;
|
|
border-radius: 6px;
|
|
overflow: hidden;
|
|
background: var(--color-gray-200);
|
|
|
|
/* Sombra interior sutil */
|
|
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);
|
|
z-index: 1;
|
|
}
|
|
|
|
.avatar-image {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
display: block;
|
|
}
|
|
|
|
.avatar-placeholder {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 2.5rem;
|
|
font-weight: 700;
|
|
color: var(--color-gray-600);
|
|
background: linear-gradient(135deg, var(--color-gray-100), var(--color-gray-300));
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
/* Modo oscuro */
|
|
.dark .avatar-container {
|
|
background: var(--color-gray-800);
|
|
}
|
|
|
|
.dark .avatar-placeholder {
|
|
background: linear-gradient(135deg, var(--color-gray-700), var(--color-gray-900));
|
|
color: var(--color-gray-300);
|
|
}
|
|
</style>
|