You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

334 lines
8.5 KiB

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mi Mascota Adorable</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css">
<style>
body,
html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
overflow: hidden;
}
body {
/* Background image will be set dynamically via JavaScript */
background-size: cover;
background-position: center;
display: flex;
flex-direction: column;
}
#app {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
.pet-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
/* background-color: rgba(0, 0, 0, 0.7); */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
opacity: 0;
animation: fadeIn 0.5s ease-in-out forwards;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.pet-text {
color: white;
padding-left: 0.25em;
padding-right: 0.25em;
font-size: 4rem;
font-weight: bold;
text-align: center;
animation: scaleUp 0.7s ease-in-out 0.3s forwards;
transform: scale(0);
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
}
@keyframes scaleUp {
from {
transform: scale(0);
}
to {
transform: scale(1);
}
}
.pet-images {
display: flex;
justify-content: center;
align-items: center;
margin-top: 2rem;
}
.pet-image {
width: 220px;
height: 220px;
border-radius: 50%;
border: 5px solid white;
background-size: cover;
background-position: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
animation: scaleIn 0.8s ease-out 0.8s forwards;
transform: scale(0);
opacity: 0;
}
@keyframes scaleIn {
from {
transform: scale(0);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
@keyframes slideInLeft {
from {
transform: translateX(-200px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideInRight {
from {
transform: translateX(200px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.pet-info {
color: white;
margin-top: 2rem;
text-align: center;
max-width: 600px;
opacity: 0;
animation: fadeIn 1s ease-in-out 1.5s forwards;
}
.social-links {
position: absolute;
bottom: 20px;
width: 100%;
text-align: center;
}
.social-links a {
color: white;
margin: 0 15px;
font-size: 2rem;
text-decoration: none;
transition: transform 0.3s ease;
}
.social-links a:hover {
transform: scale(1.2);
}
.paw-print {
position: absolute;
font-size: 2rem;
color: rgba(255, 255, 255, 0.3);
animation: float 3s infinite ease-in-out;
}
.heart-print {
position: absolute;
color: rgba(255, 105, 180, 0.3);
animation: pulse 4s infinite ease-in-out;
}
@keyframes pulse {
0% {
transform: scale(1) rotate(0deg);
opacity: 0.3;
}
50% {
transform: scale(1.3) rotate(15deg);
opacity: 0.6;
}
100% {
transform: scale(1) rotate(0deg);
opacity: 0.3;
}
}
@keyframes float {
0% {
transform: translateY(0) rotate(0deg);
}
50% {
transform: translateY(-20px) rotate(10deg);
}
100% {
transform: translateY(0) rotate(0deg);
}
}
</style>
</head>
<body>
<div id="app">
<div v-if="isLoading" class="pet-overlay">
<div class="pet-text">Loading...</div>
</div>
<div v-else-if="error" class="pet-overlay">
<div class="pet-text">Error loading configuration</div>
<div style="color: white; margin-top: 1rem; font-size: 1rem;">{{ error }}</div>
</div>
<div v-else-if="showPet" class="pet-overlay">
<!-- Decorative paw and heart prints -->
<i v-for="(paw, index) in pawPrints" :key="'paw-'+index" class="fas fa-paw paw-print"
:style="{ top: paw.top + '%', left: paw.left + '%', animationDelay: paw.delay + 's' }"></i>
<i v-for="(heart, index) in heartPrints" :key="'heart-'+index" class="fas fa-heart heart-print"
:style="{ top: heart.top + '%', left: heart.left + '%', animationDelay: heart.delay + 's', fontSize: heart.size + 'rem' }"></i>
<div class="pet-text">{{ config.matchText }}</div>
<div class="pet-images">
<div ref="centerImage" class="pet-image"></div>
</div>
<div class="pet-info">
<p>{{ config.description }}</p>
</div>
</div>
<div v-if="!isLoading && !error" class="social-links">
<a v-for="social in visibleSocialLinks" :key="social.name" :href="social.url" target="_blank">
<i :class="social.icon"></i>
</a>
</div>
</div>
<script>
const { createApp, ref, onMounted, computed, nextTick } = Vue;
createApp({
setup() {
const showPet = ref(false);
const config = ref({});
const isLoading = ref(true);
const error = ref(null);
// Template ref for the image element
const centerImage = ref(null);
// Generate random paw prints for decoration
const pawPrints = Array.from({ length: 6 }, (_, i) => ({
top: Math.random() * 100,
left: Math.random() * 100,
delay: Math.random() * 5
}));
// Generate random heart prints for decoration
const heartPrints = Array.from({ length: 6 }, (_, i) => ({
top: Math.random() * 100,
left: Math.random() * 100,
delay: Math.random() * 5,
size: 1 + Math.random() * 2.5 // Random size between 1rem and 2.5rem
}));
// Function to load configuration from data.json
const loadConfig = async () => {
try {
const dataJsonPath = './data.json';
const response = await fetch(dataJsonPath, { cache: 'no-store', headers: { 'Cache-Control': 'no-cache' } });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
config.value = data;
isLoading.value = false;
} catch (err) {
console.error('Error loading configuration:', err);
error.value = err.message;
isLoading.value = false;
}
};
// Function to set the pet image
const setMatchImages = async () => {
await nextTick(); // Wait for DOM to be updated
if (centerImage.value && config.value.centerImage) {
centerImage.value.style.backgroundImage = `url('${config.value.centerImage}')`;
}
};
// Computed property to filter social links that have URLs
const visibleSocialLinks = computed(() => {
const socialPlatforms = ['github', 'linkedin', 'twitter', 'instagram', 'facebook'];
return socialPlatforms
.map(platform => config.value[platform])
.filter(social => social && social.url && social.url.trim() !== "");
});
onMounted(async () => {
// Load configuration from data.json
await loadConfig();
// Set dynamic page title
document.title = config.value.pageTitle;
// Set dynamic background image
document.body.style.backgroundImage = `url('${config.value.backgroundImageUrl}')`;
setTimeout(async () => {
showPet.value = true;
// Set pet images after the pet overlay is shown
await setMatchImages();
}, 500);
});
return {
showPet,
config,
visibleSocialLinks,
isLoading,
error,
centerImage,
pawPrints,
heartPrints
};
}
}).mount('#app');
</script>
</body>
</html>