Visual Console Client: added an analogic clock
Former-commit-id: 642c1a39c14fe5db067d871448b3b9c8c8aa5d34
This commit is contained in:
parent
850045f791
commit
2ab938ded5
|
@ -91,14 +91,17 @@ export function clockPropsDecoder(data: UnknownObject): ClockProps | never {
|
|||
}
|
||||
|
||||
export default class Clock extends VisualConsoleItem<ClockProps> {
|
||||
public static readonly TICK_INTERVAL: number = 1000; // In ms.
|
||||
public static readonly TICK_INTERVAL = 1000; // In ms.
|
||||
private intervalRef: number | null = null;
|
||||
|
||||
public constructor(props: ClockProps) {
|
||||
// Call the superclass constructor.
|
||||
super(props);
|
||||
// The item is already loaded and inserted into the DOM.
|
||||
|
||||
// Below you can modify the item, add event handlers, timers, etc.
|
||||
/* The item is already loaded and inserted into the DOM.
|
||||
* The class properties are now initialized.
|
||||
* Now you can modify the item, add event handlers, timers, etc.
|
||||
*/
|
||||
|
||||
/* The use of the arrow function is important here. startTick will
|
||||
* use the function passed as an argument to call the global setInterval
|
||||
|
@ -109,10 +112,17 @@ export default class Clock extends VisualConsoleItem<ClockProps> {
|
|||
* use the current context at the declaration time.
|
||||
* http://es6-features.org/#Lexicalthis
|
||||
*/
|
||||
this.startTick(() => {
|
||||
// Replace the old element with the updated date.
|
||||
this.childElementRef.innerHTML = this.createClock().innerHTML;
|
||||
});
|
||||
this.startTick(
|
||||
() => {
|
||||
// Replace the old element with the updated date.
|
||||
this.childElementRef.innerHTML = this.createClock().innerHTML;
|
||||
},
|
||||
/* The analogic clock doesn't need to tick,
|
||||
* but it will be refreshed every 20 seconds
|
||||
* to avoid a desync caused by page freezes.
|
||||
*/
|
||||
this.props.clockType === "analogic" ? 20000 : Clock.TICK_INTERVAL
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -127,10 +137,16 @@ export default class Clock extends VisualConsoleItem<ClockProps> {
|
|||
|
||||
/**
|
||||
* Wrap a window.setInterval call.
|
||||
* @param handler Function to be called every time the interval
|
||||
* timer is reached.
|
||||
* @param interval Number in milliseconds for the interval timer.
|
||||
*/
|
||||
private startTick(handler: TimerHandler): void {
|
||||
private startTick(
|
||||
handler: TimerHandler,
|
||||
interval: number = Clock.TICK_INTERVAL
|
||||
): void {
|
||||
this.stopTick();
|
||||
this.intervalRef = window.setInterval(handler, Clock.TICK_INTERVAL);
|
||||
this.intervalRef = window.setInterval(handler, interval);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -174,7 +190,7 @@ export default class Clock extends VisualConsoleItem<ClockProps> {
|
|||
private createClock(): HTMLElement | never {
|
||||
switch (this.props.clockType) {
|
||||
case "analogic":
|
||||
throw new Error("not implemented.");
|
||||
return this.createAnalogicClock();
|
||||
case "digital":
|
||||
return this.createDigitalClock();
|
||||
default:
|
||||
|
@ -182,6 +198,232 @@ export default class Clock extends VisualConsoleItem<ClockProps> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a element which contains a representation of an analogic clock.
|
||||
* @return DOM Element.
|
||||
*/
|
||||
private createAnalogicClock(): HTMLElement {
|
||||
const svgNS = "http://www.w3.org/2000/svg";
|
||||
const colors = {
|
||||
watchFace: "#FFFFF0",
|
||||
watchFaceBorder: "#242124",
|
||||
mark: "#242124",
|
||||
handDark: "#242124",
|
||||
handLight: "#525252",
|
||||
secondHand: "#DC143C"
|
||||
};
|
||||
|
||||
const div = document.createElement("div");
|
||||
div.className = "analogic-clock";
|
||||
|
||||
// SVG container.
|
||||
const svg = document.createElementNS(svgNS, "svg");
|
||||
// Auto resize SVG using the view box magic: https://css-tricks.com/scale-svg/
|
||||
svg.setAttribute("viewBox", "0 0 100 100");
|
||||
|
||||
// Clock face.
|
||||
const clockFace = document.createElementNS(svgNS, "circle");
|
||||
clockFace.setAttribute("cx", "50");
|
||||
clockFace.setAttribute("cy", "50");
|
||||
clockFace.setAttribute("r", "48");
|
||||
clockFace.setAttribute("fill", colors.watchFace);
|
||||
clockFace.setAttribute("stroke", colors.watchFaceBorder);
|
||||
clockFace.setAttribute("stroke-width", "2");
|
||||
clockFace.setAttribute("stroke-linecap", "round");
|
||||
|
||||
// Marks group.
|
||||
const marksGroup = document.createElementNS(svgNS, "g");
|
||||
marksGroup.setAttribute("class", "marks");
|
||||
// Build the 12 hours mark.
|
||||
const mainMarkGroup = document.createElementNS(svgNS, "g");
|
||||
mainMarkGroup.setAttribute("class", "mark");
|
||||
mainMarkGroup.setAttribute("transform", "translate(50 50)");
|
||||
const mark1a = document.createElementNS(svgNS, "line");
|
||||
mark1a.setAttribute("x1", "36");
|
||||
mark1a.setAttribute("y1", "0");
|
||||
mark1a.setAttribute("x2", "46");
|
||||
mark1a.setAttribute("y2", "0");
|
||||
mark1a.setAttribute("stroke", colors.mark);
|
||||
mark1a.setAttribute("stroke-width", "5");
|
||||
const mark1b = document.createElementNS(svgNS, "line");
|
||||
mark1b.setAttribute("x1", "36");
|
||||
mark1b.setAttribute("y1", "0");
|
||||
mark1b.setAttribute("x2", "46");
|
||||
mark1b.setAttribute("y2", "0");
|
||||
mark1b.setAttribute("stroke", colors.watchFace);
|
||||
mark1b.setAttribute("stroke-width", "1");
|
||||
// Insert the 12 mark lines into their group.
|
||||
mainMarkGroup.append(mark1a, mark1b);
|
||||
// Insert the main mark into the marks group.
|
||||
marksGroup.append(mainMarkGroup);
|
||||
// Build the rest of the marks.
|
||||
for (let i = 1; i < 60; i++) {
|
||||
const mark = document.createElementNS(svgNS, "line");
|
||||
mark.setAttribute("y1", "0");
|
||||
mark.setAttribute("y2", "0");
|
||||
mark.setAttribute("stroke", colors.mark);
|
||||
mark.setAttribute("transform", `translate(50 50) rotate(${i * 6})`);
|
||||
|
||||
if (i % 5 === 0) {
|
||||
mark.setAttribute("x1", "38");
|
||||
mark.setAttribute("x2", "46");
|
||||
mark.setAttribute("stroke-width", i % 15 === 0 ? "2" : "1");
|
||||
} else {
|
||||
mark.setAttribute("x1", "42");
|
||||
mark.setAttribute("x2", "46");
|
||||
mark.setAttribute("stroke-width", "0.5");
|
||||
}
|
||||
|
||||
// Insert the mark into the marks group.
|
||||
marksGroup.append(mark);
|
||||
}
|
||||
|
||||
/* Clock hands */
|
||||
|
||||
// Hour hand.
|
||||
const hourHand = document.createElementNS(svgNS, "g");
|
||||
hourHand.setAttribute("class", "hour-hand");
|
||||
hourHand.setAttribute("transform", "translate(50 50)");
|
||||
// This will go back and will act like a border.
|
||||
const hourHandA = document.createElementNS(svgNS, "line");
|
||||
hourHandA.setAttribute("class", "hour-hand-a");
|
||||
hourHandA.setAttribute("x1", "0");
|
||||
hourHandA.setAttribute("y1", "0");
|
||||
hourHandA.setAttribute("x2", "30");
|
||||
hourHandA.setAttribute("y2", "0");
|
||||
hourHandA.setAttribute("stroke", colors.handLight);
|
||||
hourHandA.setAttribute("stroke-width", "4");
|
||||
hourHandA.setAttribute("stroke-linecap", "round");
|
||||
// This will go in front of the previous line.
|
||||
const hourHandB = document.createElementNS(svgNS, "line");
|
||||
hourHandB.setAttribute("class", "hour-hand-b");
|
||||
hourHandB.setAttribute("x1", "0");
|
||||
hourHandB.setAttribute("y1", "0");
|
||||
hourHandB.setAttribute("x2", "29.9");
|
||||
hourHandB.setAttribute("y2", "0");
|
||||
hourHandB.setAttribute("stroke", colors.handDark);
|
||||
hourHandB.setAttribute("stroke-width", "3.1");
|
||||
hourHandB.setAttribute("stroke-linecap", "round");
|
||||
// Append the elements to finish the hour hand.
|
||||
hourHand.append(hourHandA, hourHandB);
|
||||
|
||||
// Minute hand.
|
||||
const minuteHand = document.createElementNS(svgNS, "g");
|
||||
minuteHand.setAttribute("class", "minute-hand");
|
||||
minuteHand.setAttribute("transform", "translate(50 50)");
|
||||
// This will go back and will act like a border.
|
||||
const minuteHandA = document.createElementNS(svgNS, "line");
|
||||
minuteHandA.setAttribute("class", "minute-hand-a");
|
||||
minuteHandA.setAttribute("x1", "0");
|
||||
minuteHandA.setAttribute("y1", "0");
|
||||
minuteHandA.setAttribute("x2", "40");
|
||||
minuteHandA.setAttribute("y2", "0");
|
||||
minuteHandA.setAttribute("stroke", colors.handLight);
|
||||
minuteHandA.setAttribute("stroke-width", "2");
|
||||
minuteHandA.setAttribute("stroke-linecap", "round");
|
||||
// This will go in front of the previous line.
|
||||
const minuteHandB = document.createElementNS(svgNS, "line");
|
||||
minuteHandB.setAttribute("class", "minute-hand-b");
|
||||
minuteHandB.setAttribute("x1", "0");
|
||||
minuteHandB.setAttribute("y1", "0");
|
||||
minuteHandB.setAttribute("x2", "39.9");
|
||||
minuteHandB.setAttribute("y2", "0");
|
||||
minuteHandB.setAttribute("stroke", colors.handDark);
|
||||
minuteHandB.setAttribute("stroke-width", "1.5");
|
||||
minuteHandB.setAttribute("stroke-linecap", "round");
|
||||
const minuteHandPin = document.createElementNS(svgNS, "circle");
|
||||
minuteHandPin.setAttribute("r", "3");
|
||||
minuteHandPin.setAttribute("fill", colors.handDark);
|
||||
// Append the elements to finish the minute hand.
|
||||
minuteHand.append(minuteHandA, minuteHandB, minuteHandPin);
|
||||
|
||||
// Second hand.
|
||||
const secondHand = document.createElementNS(svgNS, "g");
|
||||
secondHand.setAttribute("class", "second-hand");
|
||||
secondHand.setAttribute("transform", "translate(50 50)");
|
||||
const secondHandBar = document.createElementNS(svgNS, "line");
|
||||
secondHandBar.setAttribute("x1", "0");
|
||||
secondHandBar.setAttribute("y1", "0");
|
||||
secondHandBar.setAttribute("x2", "46");
|
||||
secondHandBar.setAttribute("y2", "0");
|
||||
secondHandBar.setAttribute("stroke", colors.secondHand);
|
||||
secondHandBar.setAttribute("stroke-width", "1");
|
||||
secondHandBar.setAttribute("stroke-linecap", "round");
|
||||
const secondHandPin = document.createElementNS(svgNS, "circle");
|
||||
secondHandPin.setAttribute("r", "2");
|
||||
secondHandPin.setAttribute("fill", colors.secondHand);
|
||||
// Append the elements to finish the second hand.
|
||||
secondHand.append(secondHandBar, secondHandPin);
|
||||
|
||||
// Pin.
|
||||
const pin = document.createElementNS(svgNS, "circle");
|
||||
pin.setAttribute("cx", "50");
|
||||
pin.setAttribute("cy", "50");
|
||||
pin.setAttribute("r", "0.3");
|
||||
pin.setAttribute("fill", colors.handDark);
|
||||
|
||||
// Set clock time.
|
||||
const date = this.getDate();
|
||||
const hourAngle = (360 * date.getHours()) / 12 + date.getMinutes() / 2;
|
||||
const minuteAngle = (360 * date.getMinutes()) / 60;
|
||||
const secAngle = (360 * date.getSeconds()) / 60;
|
||||
|
||||
hourHand.setAttribute("transform", `translate(50 50) rotate(${hourAngle})`);
|
||||
minuteHand.setAttribute(
|
||||
"transform",
|
||||
`translate(50 50) rotate(${minuteAngle})`
|
||||
);
|
||||
secondHand.setAttribute(
|
||||
"transform",
|
||||
`translate(50 50) rotate(${secAngle})`
|
||||
);
|
||||
|
||||
// Build the clock
|
||||
svg.append(clockFace, marksGroup, hourHand, minuteHand, secondHand, pin);
|
||||
// Rotate the clock to its normal position.
|
||||
svg.setAttribute("transform", "rotate(-90)");
|
||||
|
||||
/* Add the animation declaration to the container.
|
||||
* Since the animation keyframes need to know the
|
||||
* start angle, this angle is dynamic (current time),
|
||||
* and we can't edit keyframes through javascript
|
||||
* safely and with backwards compatibility, we need
|
||||
* to inject it.
|
||||
*/
|
||||
div.innerHTML = `
|
||||
<style>
|
||||
@keyframes rotate-hour {
|
||||
from {
|
||||
transform: translate(50px, 50px) rotate(${hourAngle}deg);
|
||||
}
|
||||
to {
|
||||
transform: translate(50px, 50px) rotate(${hourAngle + 360}deg);
|
||||
}
|
||||
}
|
||||
@keyframes rotate-minute {
|
||||
from {
|
||||
transform: translate(50px, 50px) rotate(${minuteAngle}deg);
|
||||
}
|
||||
to {
|
||||
transform: translate(50px, 50px) rotate(${minuteAngle + 360}deg);
|
||||
}
|
||||
}
|
||||
@keyframes rotate-second {
|
||||
from {
|
||||
transform: translate(50px, 50px) rotate(${secAngle}deg);
|
||||
}
|
||||
to {
|
||||
transform: translate(50px, 50px) rotate(${secAngle + 360}deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
`;
|
||||
// Add the clock to the container
|
||||
div.append(svg);
|
||||
|
||||
return div;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a element which contains a representation of a digital clock.
|
||||
* @return DOM Element.
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
src: url(alarm-clock.ttf);
|
||||
}
|
||||
|
||||
/* Digital clock */
|
||||
|
||||
.visual-console-item .digital-clock {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
@ -35,3 +37,20 @@
|
|||
.visual-console-item .digital-clock > span.timezone {
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
/* Analog clock */
|
||||
|
||||
.visual-console-item .analogic-clock .hour-hand {
|
||||
-webkit-animation: rotate-hour 43200s infinite linear;
|
||||
animation: rotate-hour 43200s infinite linear;
|
||||
}
|
||||
|
||||
.visual-console-item .analogic-clock .minute-hand {
|
||||
-webkit-animation: rotate-minute 3600s infinite linear;
|
||||
animation: rotate-minute 3600s infinite linear;
|
||||
}
|
||||
|
||||
.visual-console-item .analogic-clock .second-hand {
|
||||
-webkit-animation: rotate-second 60s infinite linear;
|
||||
animation: rotate-second 60s infinite linear;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue