Rehacer avatar MSN con CSS puro en lugar de SVG
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 55s
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
This commit is contained in:
@@ -1,129 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="msn-avatar-wrapper" :class="`status-${presenceStatus}`">
|
<div class="msn-avatar-wrapper" :style="cssVars">
|
||||||
<svg
|
<div class="avatar-frame">
|
||||||
class="msn-frame"
|
|
||||||
viewBox="0 0 140 140"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
>
|
|
||||||
<!-- Definiciones -->
|
|
||||||
<defs>
|
|
||||||
<!-- Degradado radial para el marco (simula iluminación) -->
|
|
||||||
<radialGradient
|
|
||||||
id="frameGradient"
|
|
||||||
cx="25%"
|
|
||||||
cy="25%"
|
|
||||||
r="100%"
|
|
||||||
fx="15%"
|
|
||||||
fy="15%"
|
|
||||||
>
|
|
||||||
<stop offset="0%" :stop-color="colors.light" />
|
|
||||||
<stop offset="50%" :stop-color="colors.medium" />
|
|
||||||
<stop offset="100%" :stop-color="colors.dark" />
|
|
||||||
</radialGradient>
|
|
||||||
|
|
||||||
<!-- Degradado para el highlight glossy -->
|
|
||||||
<linearGradient
|
|
||||||
id="glossHighlight"
|
|
||||||
x1="0%"
|
|
||||||
y1="0%"
|
|
||||||
x2="0%"
|
|
||||||
y2="100%"
|
|
||||||
>
|
|
||||||
<stop offset="0%" stop-color="rgba(255,255,255,0.6)" />
|
|
||||||
<stop offset="40%" stop-color="rgba(255,255,255,0.2)" />
|
|
||||||
<stop offset="60%" stop-color="rgba(255,255,255,0)" />
|
|
||||||
<stop offset="100%" stop-color="rgba(0,0,0,0.1)" />
|
|
||||||
</linearGradient>
|
|
||||||
|
|
||||||
<!-- Filtro para sombra exterior -->
|
|
||||||
<filter id="dropShadow" x="-50%" y="-50%" width="200%" height="200%">
|
|
||||||
<feGaussianBlur in="SourceAlpha" stdDeviation="3" />
|
|
||||||
<feOffset dx="0" dy="2" result="offsetblur" />
|
|
||||||
<feComponentTransfer>
|
|
||||||
<feFuncA type="linear" slope="0.3" />
|
|
||||||
</feComponentTransfer>
|
|
||||||
<feMerge>
|
|
||||||
<feMergeNode />
|
|
||||||
<feMergeNode in="SourceGraphic" />
|
|
||||||
</feMerge>
|
|
||||||
</filter>
|
|
||||||
|
|
||||||
<!-- Filtro para sombra interior -->
|
|
||||||
<filter id="innerShadow" x="-50%" y="-50%" width="200%" height="200%">
|
|
||||||
<feGaussianBlur in="SourceAlpha" stdDeviation="2" result="blur" />
|
|
||||||
<feOffset in="blur" dx="1" dy="1" result="offsetBlur" />
|
|
||||||
<feFlood flood-color="rgba(0,0,0,0.4)" result="color" />
|
|
||||||
<feComposite in="color" in2="offsetBlur" operator="in" result="shadow" />
|
|
||||||
<feComposite in="shadow" in2="SourceAlpha" operator="in" result="innerShadow" />
|
|
||||||
</filter>
|
|
||||||
|
|
||||||
<!-- Clip path para el avatar (cuadrado con esquinas muy poco redondeadas) -->
|
|
||||||
<clipPath id="avatarClip">
|
|
||||||
<rect x="20" y="20" width="100" height="100" rx="3" ry="3" />
|
|
||||||
</clipPath>
|
|
||||||
|
|
||||||
<!-- Máscara para crear el marco con grosor variable -->
|
|
||||||
<mask id="frameMask">
|
|
||||||
<!-- Rectángulo exterior blanco -->
|
|
||||||
<rect x="0" y="0" width="140" height="140" fill="white" rx="10" ry="10" />
|
|
||||||
<!-- Rectángulo interior negro (el hueco del marco) -->
|
|
||||||
<!-- Usamos un path personalizado para crear el grosor variable -->
|
|
||||||
<path
|
|
||||||
d="M 20,15
|
|
||||||
Q 70,18 120,15
|
|
||||||
Q 122,70 125,120
|
|
||||||
Q 70,122 20,125
|
|
||||||
Q 18,70 15,20
|
|
||||||
Q 70,18 20,15 Z"
|
|
||||||
fill="black"
|
|
||||||
/>
|
|
||||||
</mask>
|
|
||||||
</defs>
|
|
||||||
|
|
||||||
<!-- Marco exterior con grosor variable -->
|
|
||||||
<g filter="url(#dropShadow)">
|
|
||||||
<!-- Base del marco con degradado radial -->
|
|
||||||
<rect
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="140"
|
|
||||||
height="140"
|
|
||||||
rx="10"
|
|
||||||
ry="10"
|
|
||||||
fill="url(#frameGradient)"
|
|
||||||
mask="url(#frameMask)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Capa glossy superior -->
|
|
||||||
<rect
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="140"
|
|
||||||
height="140"
|
|
||||||
rx="10"
|
|
||||||
ry="10"
|
|
||||||
fill="url(#glossHighlight)"
|
|
||||||
mask="url(#frameMask)"
|
|
||||||
opacity="0.8"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Highlight especular en la esquina superior izquierda -->
|
|
||||||
<ellipse
|
|
||||||
cx="25"
|
|
||||||
cy="25"
|
|
||||||
rx="15"
|
|
||||||
ry="12"
|
|
||||||
fill="rgba(255,255,255,0.7)"
|
|
||||||
mask="url(#frameMask)"
|
|
||||||
opacity="0.9"
|
|
||||||
/>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
<!-- Avatar del usuario -->
|
|
||||||
<div class="avatar-container">
|
<div class="avatar-container">
|
||||||
<img
|
<img
|
||||||
v-if="src"
|
v-if="src && !imageError"
|
||||||
:src="src"
|
:src="src"
|
||||||
:alt="alt"
|
:alt="alt"
|
||||||
class="avatar-image"
|
class="avatar-image"
|
||||||
@@ -134,6 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -173,6 +54,14 @@ const colors = computed(() => {
|
|||||||
const { PRESENCE_COLORS } = usePresence()
|
const { PRESENCE_COLORS } = usePresence()
|
||||||
return PRESENCE_COLORS[props.presenceStatus]
|
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>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@@ -181,27 +70,83 @@ const colors = computed(() => {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: var(--avatar-size, 128px);
|
width: var(--avatar-size, 128px);
|
||||||
height: var(--avatar-size, 128px);
|
height: var(--avatar-size, 128px);
|
||||||
|
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.msn-frame {
|
.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;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
right: 0;
|
||||||
height: 100%;
|
height: 50%;
|
||||||
z-index: 2;
|
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;
|
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 {
|
.avatar-container {
|
||||||
position: absolute;
|
position: relative;
|
||||||
top: 14.3%; /* Proporción del marco */
|
width: 100%;
|
||||||
left: 14.3%;
|
height: 100%;
|
||||||
width: 71.4%; /* 100% - 2*14.3% */
|
border-radius: 6px;
|
||||||
height: 71.4%;
|
|
||||||
border-radius: 2px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: var(--color-gray-200);
|
background: var(--color-gray-200);
|
||||||
|
|
||||||
|
/* Sombra interior sutil */
|
||||||
|
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,36 +163,11 @@ const colors = computed(() => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-size: 2rem;
|
font-size: 2.5rem;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
color: var(--color-gray-600);
|
color: var(--color-gray-600);
|
||||||
background: linear-gradient(135deg, var(--color-gray-100), var(--color-gray-300));
|
background: linear-gradient(135deg, var(--color-gray-100), var(--color-gray-300));
|
||||||
}
|
text-transform: uppercase;
|
||||||
|
|
||||||
/* Variaciones de tamaño */
|
|
||||||
.msn-avatar-wrapper.size-sm {
|
|
||||||
--avatar-size: 64px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.msn-avatar-wrapper.size-md {
|
|
||||||
--avatar-size: 96px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.msn-avatar-wrapper.size-lg {
|
|
||||||
--avatar-size: 128px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.msn-avatar-wrapper.size-xl {
|
|
||||||
--avatar-size: 160px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Animación de hover */
|
|
||||||
.msn-avatar-wrapper {
|
|
||||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.msn-avatar-wrapper:hover {
|
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Modo oscuro */
|
/* Modo oscuro */
|
||||||
|
|||||||
Reference in New Issue
Block a user