155 lines
3.1 KiB
Vue
155 lines
3.1 KiB
Vue
|
|
<template>
|
||
|
|
<div ref="counterElement" class="years-counter-badge">
|
||
|
|
<span class="emoji">💻</span>
|
||
|
|
<div class="counter-box">
|
||
|
|
<span class="number">{{ displayYear }}</span>
|
||
|
|
</div>
|
||
|
|
<span class="label"></span>
|
||
|
|
</div>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script setup>
|
||
|
|
import { ref } from "vue";
|
||
|
|
import { onSlideEnter } from "@slidev/client";
|
||
|
|
|
||
|
|
const props = defineProps({
|
||
|
|
startYear: {
|
||
|
|
type: Number,
|
||
|
|
default: 1985,
|
||
|
|
},
|
||
|
|
speed: {
|
||
|
|
type: Number,
|
||
|
|
default: 80,
|
||
|
|
},
|
||
|
|
delay: {
|
||
|
|
type: Number,
|
||
|
|
default: 3000, // 3 segundos de delay por defecto
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
const displayYear = ref(1);
|
||
|
|
const animationKey = ref(0);
|
||
|
|
const targetYear = new Date().getFullYear() - props.startYear;
|
||
|
|
const hasStarted = ref(false);
|
||
|
|
let interval = null;
|
||
|
|
|
||
|
|
const startAnimation = () => {
|
||
|
|
if (hasStarted.value) return; // Solo iniciar una vez
|
||
|
|
|
||
|
|
hasStarted.value = true;
|
||
|
|
|
||
|
|
// Esperar el delay configurado antes de iniciar
|
||
|
|
setTimeout(() => {
|
||
|
|
let year = 1;
|
||
|
|
interval = setInterval(() => {
|
||
|
|
if (year <= targetYear) {
|
||
|
|
displayYear.value = year;
|
||
|
|
animationKey.value = year * 0.001; // Trigger animation
|
||
|
|
year++;
|
||
|
|
} else {
|
||
|
|
clearInterval(interval);
|
||
|
|
}
|
||
|
|
}, props.speed);
|
||
|
|
}, props.delay);
|
||
|
|
};
|
||
|
|
|
||
|
|
// Usar onSlideEnter de Slidev para iniciar la animación cuando la diapositiva es visible
|
||
|
|
onSlideEnter(() => {
|
||
|
|
startAnimation();
|
||
|
|
});
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style scoped>
|
||
|
|
.years-counter-badge {
|
||
|
|
display: inline-flex;
|
||
|
|
align-items: center;
|
||
|
|
gap: 0.75rem;
|
||
|
|
padding: 0.75rem 1.5rem;
|
||
|
|
/*background: linear-gradient(
|
||
|
|
135deg,
|
||
|
|
rgba(72, 187, 120, 0.15),
|
||
|
|
rgba(72, 187, 120, 0.25)
|
||
|
|
);
|
||
|
|
border: 2px solid rgba(72, 187, 120, 0.5);
|
||
|
|
*/
|
||
|
|
border-radius: 12px;
|
||
|
|
backdrop-filter: blur(10px);
|
||
|
|
box-shadow: 0 4px 20px rgba(206, 66, 43, 0.2);
|
||
|
|
font-size: 1rem;
|
||
|
|
animation: pulse 3s ease-in-out infinite;
|
||
|
|
}
|
||
|
|
|
||
|
|
.emoji {
|
||
|
|
font-size: 1.5rem;
|
||
|
|
line-height: 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
.counter-box {
|
||
|
|
background: rgba(0, 0, 0, 0.3);
|
||
|
|
border-radius: 8px;
|
||
|
|
padding: 0.25rem 0.75rem;
|
||
|
|
min-width: 3rem;
|
||
|
|
text-align: center;
|
||
|
|
}
|
||
|
|
|
||
|
|
.number {
|
||
|
|
font-size: 2rem;
|
||
|
|
font-weight: bold;
|
||
|
|
/*color: #4ade80;*/
|
||
|
|
color: rgba(206, 66, 43);
|
||
|
|
line-height: 1;
|
||
|
|
display: inline-block;
|
||
|
|
animation: flip 0.3s ease-in-out;
|
||
|
|
}
|
||
|
|
|
||
|
|
.label {
|
||
|
|
font-size: 0.95rem;
|
||
|
|
font-weight: 600;
|
||
|
|
color: #e5e7eb;
|
||
|
|
white-space: nowrap;
|
||
|
|
}
|
||
|
|
|
||
|
|
@keyframes pulse {
|
||
|
|
0%,
|
||
|
|
100% {
|
||
|
|
opacity: 1;
|
||
|
|
transform: scale(1);
|
||
|
|
}
|
||
|
|
50% {
|
||
|
|
opacity: 0.9;
|
||
|
|
transform: scale(0.98);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@keyframes flip {
|
||
|
|
0%,
|
||
|
|
100% {
|
||
|
|
transform: scaleY(1);
|
||
|
|
}
|
||
|
|
50% {
|
||
|
|
transform: scaleY(0.3);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Responsive adjustments */
|
||
|
|
@media (max-width: 768px) {
|
||
|
|
.years-counter-badge {
|
||
|
|
font-size: 0.875rem;
|
||
|
|
padding: 0.5rem 1rem;
|
||
|
|
gap: 0.5rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.emoji {
|
||
|
|
font-size: 1.25rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.number {
|
||
|
|
font-size: 1.5rem;
|
||
|
|
}
|
||
|
|
|
||
|
|
.label {
|
||
|
|
font-size: 0.8rem;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
</style>
|