class SwissClock { constructor(elementId, useUTC = false) { this.element = document.getElementById(elementId); this.useUTC = useUTC; if (!this.element) { console.error(`SwissClock : élément "${elementId}" introuvable.`); return; } this.createClock(); this.update(); } createClock() { this.element.innerHTML = ""; // SVG this.svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); this.svg.setAttribute("viewBox", "0 0 100 100"); this.svg.setAttribute("width", "100%"); this.svg.setAttribute("height", "100%"); this.svg.setAttribute("aria-label", "Horloge suisse"); // Style global this.svg.style.display = "block"; this.svg.style.overflow = "visible"; // Groupe principal this.clockGroup = document.createElementNS( "http://www.w3.org/2000/svg", "g", ); // ------------------------------------------------------------ // CADRAN // ------------------------------------------------------------ const face = document.createElementNS( "http://www.w3.org/2000/svg", "circle", ); face.setAttribute("cx", "50"); face.setAttribute("cy", "50"); face.setAttribute("r", "47"); face.setAttribute("fill", "#ffffff"); this.clockGroup.appendChild(face); // ------------------------------------------------------------ // INDEX / MINUTES // ------------------------------------------------------------ for (let i = 0; i < 60; i++) { const angle = ((i * 6 - 90) * Math.PI) / 180; const isHour = i % 5 === 0; const outerRadius = 44; const innerRadius = isHour ? 36 : 41; const x1 = 50 + Math.cos(angle) * innerRadius; const y1 = 50 + Math.sin(angle) * innerRadius; const x2 = 50 + Math.cos(angle) * outerRadius; const y2 = 50 + Math.sin(angle) * outerRadius; const tick = document.createElementNS( "http://www.w3.org/2000/svg", "line", ); tick.setAttribute("x1", x1); tick.setAttribute("y1", y1); tick.setAttribute("x2", x2); tick.setAttribute("y2", y2); tick.setAttribute("stroke", "#111111"); tick.setAttribute("stroke-width", isHour ? "2.2" : "0.8"); tick.setAttribute("stroke-linecap", isHour ? "square" : "round"); this.clockGroup.appendChild(tick); } // ------------------------------------------------------------ // AIGUILLE DES HEURES // ------------------------------------------------------------ this.hourHand = document.createElementNS( "http://www.w3.org/2000/svg", "line", ); this.hourHand.setAttribute("x1", "50"); this.hourHand.setAttribute("y1", "52"); this.hourHand.setAttribute("x2", "50"); this.hourHand.setAttribute("y2", "25"); this.hourHand.setAttribute("stroke", "#111111"); this.hourHand.setAttribute("stroke-width", "5"); this.hourHand.setAttribute("stroke-linecap", "round"); this.clockGroup.appendChild(this.hourHand); // ------------------------------------------------------------ // AIGUILLE DES MINUTES // ------------------------------------------------------------ this.minuteHand = document.createElementNS( "http://www.w3.org/2000/svg", "line", ); this.minuteHand.setAttribute("x1", "50"); this.minuteHand.setAttribute("y1", "53"); this.minuteHand.setAttribute("x2", "50"); this.minuteHand.setAttribute("y2", "15"); this.minuteHand.setAttribute("stroke", "#111111"); this.minuteHand.setAttribute("stroke-width", "3.5"); this.minuteHand.setAttribute("stroke-linecap", "round"); this.clockGroup.appendChild(this.minuteHand); // ------------------------------------------------------------ // TROTTEUSE ROUGE // ------------------------------------------------------------ this.secondHand = document.createElementNS( "http://www.w3.org/2000/svg", "line", ); this.secondHand.setAttribute("x1", "50"); this.secondHand.setAttribute("y1", "55"); this.secondHand.setAttribute("x2", "50"); this.secondHand.setAttribute("y2", "10"); this.secondHand.setAttribute("stroke", "#e30613"); this.secondHand.setAttribute("stroke-width", "1.5"); this.secondHand.setAttribute("stroke-linecap", "round"); this.clockGroup.appendChild(this.secondHand); // ------------------------------------------------------------ // CONTREPOIDS DE LA TROTTEUSE // ------------------------------------------------------------ this.secondCounterweight = document.createElementNS( "http://www.w3.org/2000/svg", "line", ); this.secondCounterweight.setAttribute("x1", "50"); this.secondCounterweight.setAttribute("y1", "48"); this.secondCounterweight.setAttribute("x2", "50"); this.secondCounterweight.setAttribute("y2", "61"); this.secondCounterweight.setAttribute("stroke", "#e30613"); this.secondCounterweight.setAttribute("stroke-width", "2"); this.secondCounterweight.setAttribute("stroke-linecap", "round"); this.clockGroup.appendChild(this.secondCounterweight); // ------------------------------------------------------------ // CENTRE // ------------------------------------------------------------ const centerOuter = document.createElementNS( "http://www.w3.org/2000/svg", "circle", ); centerOuter.setAttribute("cx", "50"); centerOuter.setAttribute("cy", "50"); centerOuter.setAttribute("r", "3"); centerOuter.setAttribute("fill", "#111111"); this.clockGroup.appendChild(centerOuter); const centerInner = document.createElementNS( "http://www.w3.org/2000/svg", "circle", ); centerInner.setAttribute("cx", "50"); centerInner.setAttribute("cy", "50"); centerInner.setAttribute("r", "1.3"); centerInner.setAttribute("fill", "#e30613"); this.clockGroup.appendChild(centerInner); // ------------------------------------------------------------ // BORDURE // ------------------------------------------------------------ const border = document.createElementNS( "http://www.w3.org/2000/svg", "circle", ); border.setAttribute("cx", "50"); border.setAttribute("cy", "50"); border.setAttribute("r", "47"); border.setAttribute("fill", "none"); border.setAttribute("stroke", "#111111"); border.setAttribute("stroke-width", "1.5"); this.clockGroup.appendChild(border); // ------------------------------------------------------------ // AJOUT AU SVG // ------------------------------------------------------------ this.svg.appendChild(this.clockGroup); this.element.appendChild(this.svg); } getTime() { const now = new Date(); if (this.useUTC) { return { hours: now.getUTCHours(), minutes: now.getUTCMinutes(), seconds: now.getUTCSeconds(), milliseconds: now.getUTCMilliseconds(), }; } return { hours: now.getHours(), minutes: now.getMinutes(), seconds: now.getSeconds(), milliseconds: now.getMilliseconds(), }; } update() { const time = this.getTime(); // ------------------------------------------------------------ // ANGLES // ------------------------------------------------------------ // Heure const hourAngle = (time.hours % 12) * 30 + time.minutes * 0.5; // Minute const minuteAngle = time.minutes * 6 + time.seconds * 0.1; // Seconde const secondAngle = time.seconds * 6; // ------------------------------------------------------------ // ROTATION DES AIGUILLES // ------------------------------------------------------------ this.hourHand.setAttribute("transform", `rotate(${hourAngle} 50 50)`); this.minuteHand.setAttribute("transform", `rotate(${minuteAngle} 50 50)`); this.secondHand.setAttribute("transform", `rotate(${secondAngle} 50 50)`); this.secondCounterweight.setAttribute( "transform", `rotate(${secondAngle} 50 50)`, ); // ------------------------------------------------------------ // PROCHAIN RAF // ------------------------------------------------------------ requestAnimationFrame(() => this.update()); } }