Files

281 lines
8.1 KiB
JavaScript
Raw Permalink Normal View History

2026-08-21 11:03:00 +02:00
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());
}
}