Files
twitch-subathon-timer/timer.js
2025-09-02 22:28:58 +10:00

190 lines
4.7 KiB
JavaScript

window.addEventListener("DOMContentLoaded", () => {
const websocket = new WebSocket("ws://adamwalsh.name:9533");
websocket.addEventListener("open", () => {
communicate(websocket);
});
});
class Timer {
constructor(renderer, end_time, running) {
this.renderer = renderer;
this.end_time = end_time;
this.running = running;
this.destroying = false;
}
render_time() {
// Render timer status to the document
var timestr;
if (this.counter < 0) {
timestr = "--:--";
} else {
// How many seconds have passed in this block
var dur_seconds = Math.floor( (this.end_time - new Date()) / 1000);
var seconds = dur_seconds % 60;
dur_seconds = dur_seconds - seconds;
var minutes = Math.floor(dur_seconds / 60);
var hours = Math.floor(minutes / 60);
minutes = minutes - 60 * hours;
timestr = String(hours).padStart(2, '0') + ':' + String(minutes).padStart(2, '0');
}
if (this.running) {
this.renderer.update_time(timestr);
}
}
tick() {
// Recursive call run per second to update and change stage
if (this.destroying) {
return
}
this.render_time();
setTimeout(this.tick.bind(this), 1000);
}
}
class TimerRenderer {
constructor (){
this.background = document.getElementsByClassName("timer-bg")[0];
this.time_element = document.getElementsByClassName("timer-time")[0];
this.bg_width = parseInt(
window.getComputedStyle(
this.background, null
).getPropertyValue('width'),
10
);
this.bg_height = parseInt(
window.getComputedStyle(
this.background, null
).getPropertyValue('height'),
10
)
}
update_time (timestr) {
this.time_element.textContent = timestr;
}
set_break_color () {
this.background.style.setProperty('--stage-color', 'var(--break-color)');
this.time_element.style.color = "var(--break-color)";
}
set_focus_color () {
this.background.style.setProperty('--stage-color', 'var(--focus-color)');
this.time_element.style.color = "var(--focus-color)";
}
}
function rectBounds(prop, rectHeight, rectWidth) {
var totalCircum = 2 * rectHeight + 2 * rectWidth;
var propCircum = prop * totalCircum;
let bounds;
if (propCircum < rectWidth / 2) {
var xprop = Math.floor((
(rectWidth/2 + propCircum) / rectWidth
) * 1000)/10;
bounds = [
"50% -5%",
`${xprop}% -5%`,
`${xprop}% 4%`,
"50% 4%"
];
} else if (propCircum < rectWidth / 2 + rectHeight) {
var yprop = (
(propCircum - rectWidth / 2) / rectHeight
) * 100;
bounds = [
"50% -5%", "110% -5%",
`110% ${yprop}%`, `96% ${yprop}%`,
"95% 4%", "50% 4%"
];
} else if (propCircum < 3 * rectWidth / 2 + rectHeight) {
var xprop = (
((3 * rectWidth/2 + rectHeight) - propCircum) / rectWidth
) * 100;
bounds = [
"50% -5%", "110% -5%",
"110% 110%",
`${xprop}% 110%`, `${xprop}% 96%`,
"95% 96%",
"95% 4%", "50% 4%"
];
} else if (propCircum < 3 * rectWidth / 2 + 2 * rectHeight) {
var yprop = (
((3 * rectWidth/2 + 2 * rectHeight) - propCircum) / rectHeight
) * 100;
bounds = [
"50% -5%", "110% -5%",
"110% 110%", "-5% 110%",
`-5% ${yprop}%`, `4% ${yprop}%`,
"5% 96%", "95% 96%",
"95% 4%", "50% 4%"
];
} else {
var xprop = (
(propCircum - (3 * rectWidth/2 + 2 * rectHeight)) / rectWidth
) * 100;
bounds = [
"50% -5%", "110% -5%",
"110% 110%", "-5% 110%",
"-5% -5%",
`${xprop}% -5%`, `${xprop}% 4%`,
"4% 4%",
"5% 96%", "95% 96%",
"95% 4%", "50% 4%"
];
}
return bounds;
}
function communicate(websocket) {
console.log("Communicating");
const params = new URLSearchParams(window.location.search);
websocket.send(JSON.stringify({type: "init", channel: "SubTimer", channelid: params.get('channelid')}));
var renderer = new TimerRenderer();
let timer;
websocket.addEventListener("message", ({ data }) => {
console.log("Rec Event " + data);
const event = JSON.parse(data);
switch (event.type) {
case "DO":
let args = event.args;
// Call the specified method
switch (event.method) {
case "setTimer":
if (timer != null) {
timer.destroying = true;
}
timer = new Timer(
renderer,
new Date(args.end_at),
true
)
timer.tick();
timer.running = args.running;
break;
default:
throw new Error(`Unsupported method requested: ${event.method}.`)
}
break;
default:
throw new Error(`Unsupported event type: ${event.type}.`);
}
});
}