first commit

This commit is contained in:
mrtoine 2025-09-12 10:57:48 +02:00
commit b216a187bd
34 changed files with 4829 additions and 0 deletions

301
Static/js/main.js Normal file
View file

@ -0,0 +1,301 @@
import { readJson } from './readJson.js';
document.addEventListener('DOMContentLoaded', async function() {
const navbar = document.querySelector("nav");
const header = document.querySelector("header");
let pagePath = "";
const linkInterceptor = () => {
const a = document.querySelectorAll('a');
a.forEach(function(link) {
if( link.getAttribute('data-page') != "externe") {
link.addEventListener('click', function(event) {
event.preventDefault();
const href = link.getAttribute('data-page');
const id = link.getAttribute('id');
if (!href) {
console.warn('Attribut data-page introuvable sur la balise:', link);
return;
}
if(id != "admin") {
loadPage(`./Views/${href}.html`);
} else {
window.location.href = href;
}
});
}
});
}
const loadPage = async (page) => {
const mainContent = document.querySelector('.main-content');
const loadProjects = async () => {
try {
const data = await readJson('./data/projects.json');
return data;
} catch (error) {
console.error('Erreur lors du chargement des projets :', error);
return [];
}
}
const loadDataContacts = async () => {
try {
const data = await readJson('./data/contacts.json');
return data;
} catch (error) {
console.error('Erreur lors du chargement des projets :', error);
return [];
}
}
if (!mainContent) {
console.error('Élément .main-content introuvable dans le document.');
return;
}
if(mainContent) {
fetch(page)
.then(response => {
if(!response.ok) {
throw new Error(`Erreur lors du chargement de la page : ${response.statusText}`);
}
pagePath = page;
return response.text();
})
.then(async (html) => {
mainContent.innerHTML = html;
linkInterceptor();
const divProjectsGrid = document.querySelector('.projects-grid');
if (divProjectsGrid) {
console.log("Projects grid found, loading projects...");
const data = await loadProjects();
if (data) {
const projects = Object.values(data);
console.log('Projets :', projects);
projects.forEach(project => {
if (project.active) {
const projectDiv = document.createElement('div');
projectDiv.className = 'project-card';
projectDiv.innerHTML = `
<div class="project-card-header">
<h3>${project.type}</h3>
</div>
<div class="project-card-content">
<h4>${project.name}</h4>
<p>${project.description}</p>
${project.link ? `<center><a href="${project.link}" target="_blank" class="btn">Voir le projet</a></center>` : ''}
<p></p>
<div class="project-tags">
${project.technologies && project.technologies.length > 0 ?
project.technologies.map(tech => `<span class="tag">${tech}</span>`).join('') : ''}
</div>
</div>
`;
divProjectsGrid.appendChild(projectDiv);
}
});
}
}
const contactPage = document.querySelector('.contact-page');
if (contactPage) {
console.log("Contact page found, loading contact data...");
const data = await loadDataContacts();
if (data) {
const email = document.querySelector('[data-id="email"]');
const phone = document.querySelector('[data-id="phone"]');
const github = document.querySelector('[data-id="github"]');
const linkedin = document.querySelector('[data-id="linkedin"]');
const twitter = document.querySelector('[data-id="twitter"]');
email.textContent = data.email;
email.href = `mailto:${data.email}`;
phone.textContent = data.gsm;
phone.href = `tel:${data.gsm}`;
github.href = data.github;
linkedin.href = data.linkedin;
twitter.href = data.twitter;
}
}
});
}
}
// Animation pour l'en-tête au défilement
window.addEventListener('scroll', () => {
if (window.scrollY > 50) {
navbar.classList.add('scrolled');
} else {
navbar.classList.remove('scrolled');
}
});
// Ajout de la détection du mode sombre
const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)");
if (prefersDarkScheme.matches) {
document.body.classList.add("dark-theme");
}
// Gestion des formulaires
document.addEventListener('submit', (e) => {
const form = e.target.closest('form');
if (form) {
e.preventDefault();
// Simuler l'envoi du formulaire
const submitBtn = form.querySelector('button[type="submit"]');
if (submitBtn) {
const originalText = submitBtn.textContent;
submitBtn.textContent = 'Envoi en cours...';
submitBtn.disabled = true;
setTimeout(() => {
form.innerHTML = `<div style="text-align: center; padding: 2rem;">
<h3 style="color: var(--success-color);">Message envoyé avec succès!</h3>
<p>Merci pour votre message. Je vous répondrai dans les plus brefs délais.</p>
</div>`;
}, 1500);
}
}
});
// Animation des particules dans le header
const createParticles = () => {
const particlesContainer = document.getElementById('particles');
if (!particlesContainer) return;
// Nombre de particules
const particleCount = 30;
// Supprimer les particules existantes
particlesContainer.innerHTML = '';
// Créer de nouvelles particules
for (let i = 0; i < particleCount; i++) {
const particle = document.createElement('span');
particle.classList.add('particle');
// Attributs aléatoires pour chaque particule
const size = Math.random() * 15 + 5;
const posX = Math.random() * 100;
const posY = Math.random() * 100;
const delay = Math.random() * 5;
const duration = Math.random() * 5 + 5;
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
particle.style.left = `${posX}%`;
particle.style.top = `${posY}%`;
particle.style.animation = `float ${duration}s infinite ${delay}s`;
particle.style.opacity = Math.random() * 0.5 + 0.3;
particlesContainer.appendChild(particle);
}
};
// Effet d'écriture au clavier
const initTypeWriter = () => {
const textElement = document.getElementById('typing-text');
if (!textElement) return;
const phrases = [
"Développeur Web & Applications",
"Développeur Python",
"Développeur JavaScript",
"Développeur C# & Unity",
"Développeur EmberJS",
"Développeur Typescript",
"Développeur Angular",
];
let phraseIndex = 0;
let charIndex = 0;
let isDeleting = false;
let typingSpeed = 100;
let isWaiting = false;
function typeWriter() {
if (isWaiting) {
setTimeout(typeWriter, typingSpeed);
isWaiting = false;
return;
}
const currentPhrase = phrases[phraseIndex];
if (isDeleting) {
// Effacer le texte
textElement.textContent = currentPhrase.substring(0, charIndex - 1);
charIndex--;
typingSpeed = 30; // Plus rapide pour effacer
} else {
// Écrire le texte
textElement.textContent = currentPhrase.substring(0, charIndex + 1);
charIndex++;
// Variation aléatoire de la vitesse pour un effet plus naturel
typingSpeed = Math.random() * 50 + 80;
}
// Si toute la phrase est écrite
if (!isDeleting && charIndex === currentPhrase.length) {
// Pause avant d'effacer
isDeleting = true;
typingSpeed = 2000; // Pause plus longue
isWaiting = true;
}
// Si la phrase est effacée
if (isDeleting && charIndex === 0) {
isDeleting = false;
phraseIndex = (phraseIndex + 1) % phrases.length;
typingSpeed = 700; // Pause avant la prochaine phrase
isWaiting = true;
}
setTimeout(typeWriter, typingSpeed);
}
typeWriter();
// Effet d'apparition du curseur
const cursor = document.querySelector('.cursor');
if (cursor) {
cursor.style.animation = 'blink 1s step-end infinite';
}
};
// Effet de défilement doux lorsqu'on clique sur le header
if (header) {
header.addEventListener('click', () => {
// Défilement vers la section suivante
const nextSection = document.querySelector('nav');
if (nextSection) {
window.scrollTo({
top: nextSection.offsetTop,
behavior: 'smooth'
});
}
});
// Effet visuel au survol pour indiquer que le header est cliquable
header.style.cursor = 'pointer';
header.addEventListener('mouseenter', () => {
header.style.transform = 'scale(1.01)';
});
header.addEventListener('mouseleave', () => {
header.style.transform = 'scale(1)';
});
}
// Initialiser les particules et l'effet d'écriture
createParticles();
initTypeWriter();
loadPage('Views/home.html');
});