v4-Animation rework

- added JS logic for controlling animations based on text length

- added duplicate elements for looping scroll effect and JS logic for populating them
This commit is contained in:
2025-11-02 03:45:11 -05:00
parent 960c037362
commit da6ae0b9c3
4 changed files with 227 additions and 57 deletions

View File

@@ -13,53 +13,70 @@
<body> <body>
<main> <main>
<div class='contentContainer'> <div class='contentContainer'>
<img class='mainHeart' src='src/youve_got_mail_whiteheart.png'>
<img class='mainHeart' src='src/youvegotmail.webp'>
<div class='infoWrap'> <div class='infoWrap'>
<div class='infoGroup'> <div class='infoGroup'>
<div class='infoBox' id='InfoBox3'> <div class='infoBox' id='InfoBox3'>
<p id='TopUsers'> <p id='TopUsers'>
Leaderboard: Leaderboard:
</p> </p>
<div class='giftUserWrap'>
<div class='giftUserRow' id='GiftUserRow1'> <div class='rightFadeWrap' id='RightFadeWrapInfoBox3'>
<p class='giftUserNum' id='GiftUserNum1'> <div class='giftUserWrap' id='GiftUserWrap'>
1 <div class='giftUserRow' id='GiftUserRow1'>
</p> <p class='giftUserNum' id='GiftUserNum1'>
<div class='scrollWrap' id='ScrollWrapName1'> 1
<div class='scrollWrapW' id='ScrollWrapNameW1'> </p>
<p class='giftUserName'id='GiftUserName1'> <div class='scrollWrap' id='ScrollWrapName1'>
Usernamethatisverylong <div class='scrollWrapW' id='ScrollWrapNameW1'>
</p> <p class='giftUserName'id='GiftUserName1'>
Usernamethatisverylong
</p>
<p class='giftUserNameDupe'id='GiftUserNameDupe1'>
Usernamethatisverylong
</p>
</div>
</div> </div>
</div> </div>
</div> <div class='giftUserRow' id='GiftUserRow2'>
<div class='giftUserRow' id='GiftUserRow2'> <p class='giftUserNum' id='GiftUserNum2'>
<p class='giftUserNum' id='GiftUserNum2'> 2
2 </p>
</p> <div class='scrollWrap' id='ScrollWrapName2'>
<div class='scrollWrap' id='ScrollWrapName2'> <div class='scrollWrapW' id='ScrollWrapNameW2'>
<div class='scrollWrapW' id='ScrollWrapNameW2'> <p class='giftUserName'id='GiftUserName2'>
<p class='giftUserName'id='GiftUserName2'> Usernamethatisverylong
Usernamethatisverylong </p>
</p> <p class='giftUserNameDupe'id='GiftUserNameDupe2'>
Usernamethatisverylong
</p>
</div>
</div> </div>
</div> </div>
</div> <div class='giftUserRow' id='GiftUserRow3'>
<div class='giftUserRow' id='GiftUserRow3'> <p class='giftUserNum' id='GiftUserNum3'>
<p class='giftUserNum' id='GiftUserNum3'> 3
3 </p>
</p> <div class='scrollWrap' id='ScrollWrapName3'>
<div class='scrollWrap' id='ScrollWrapName3'> <div class='scrollWrapW' id='ScrollWrapNameW3'>
<div class='scrollWrapW' id='ScrollWrapNameW3'> <p class='giftUserName'id='GiftUserName3'>
<p class='giftUserName'id='GiftUserName3'> Usernamethatisverylong
Usernamethatisverylong </p>
</p> <p class='giftUserNameDupe'id='GiftUserNameDupe3'>
Usernamethatisverylong
</p>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<img class='mainHeart' id='SecondHeart' src='src/youve_got_mail_whiteheart.png'>
<img class='mainHeart' id='SecondHeart' src='src/youvegotmail.webp'>
<div class='infoBox' id='InfoBox1'> <div class='infoBox' id='InfoBox1'>
<h1 id='Timer'> <h1 id='Timer'>
00:00 00:00
@@ -68,15 +85,23 @@
!subathon for details !subathon for details
</p> </p>
</div> </div>
<div class='infoBox' id='InfoBox2'> <div class='infoBox' id='InfoBox2'>
<p id='GoalLabel'> <p id='GoalLabel'>
Next Goal: Next Goal:
</p> </p>
<div class='scrollWrap' id='ScrollWrapGoal'> <div class='rightFadeWrap' id='RightFadeWrapInfoBox2'>
<div class='scrollWrapW' id='ScrollWrapGoalW'> <div class='scrollWrap' id='ScrollWrapGoal'>
<p id='GoalName'> <div class='scrollWrapW' id='ScrollWrapGoalW'>
Discord Watch Party + Puzzles <p id='GoalName'>
</p> Discord Watch Party + Puzzles
</p>
<p id='GoalNameDupe'>
Discord Watch Party + Puzzles
</p>
</div>
</div> </div>
</div> </div>
<div id='GoalProgressWrap'> <div id='GoalProgressWrap'>
@@ -85,6 +110,7 @@
</p> </p>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>

BIN
src/youvegotmail.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@@ -102,6 +102,7 @@ p {
} }
#InfoBox1 { #InfoBox1 {
} }
#InfoBox2 { #InfoBox2 {
@@ -116,6 +117,13 @@ p {
border: .4cqh solid; border: .4cqh solid;
border-color: #00A0F3; border-color: #00A0F3;
background-color: white; background-color: white;
}
.rightFadeWrap {
mask-image: linear-gradient(to right, black 85%, transparent 100%);
}
#RightFadeWrapInfoBox3 {
} }
@@ -140,7 +148,20 @@ p {
position: relative; position: relative;
color: #32B993; color: #32B993;
text-wrap: nowrap; text-wrap: nowrap;
animation: myScroll 12s linear infinite; display: inline-block;
padding-right: 4%;
transform: translate(.5ch, 0%);
/* --- animation-duration = JS VAR --- */
}
#GoalNameDupe {
width: fit-content;
position: relative;
color: #32B993;
text-wrap: nowrap;
padding-right: 4%;
transform: translate(.5ch, 0%);
/* --- animation-duration = JS VAR --- */
} }
#GoalProgressWrap { #GoalProgressWrap {
@@ -161,7 +182,6 @@ p {
#TopUsers { #TopUsers {
margin-bottom: 2%; margin-bottom: 2%;
transform: translate(0%, 0%) scale(1);
font-weight: normal; font-weight: normal;
color: #404145; color: #404145;
} }
@@ -187,21 +207,55 @@ p {
position: relative; position: relative;
color: #32B993; color: #32B993;
text-align: left; text-align: left;
display: inline-block;
padding-right: 4%;
transform: translate(.5ch, 0%);
}
.giftUserNameDupe {
width: fit-content;
margin-left: 0%;
position: relative;
color: #32B993;
text-align: left;
padding-right: 4%;
transform: translate(.5ch, 0%);
} }
#GiftUserName1 { #GiftUserName1 {
animation: myScroll 9s linear infinite; /* --- animation-duration = JS VAR --- */
} }
#GiftUserName2 { #GiftUserName2 {
animation: myScroll 9s 3s linear infinite; /* --- animation-duration = JS VAR --- */
animation-delay: 3s;
} }
#GiftUserName3 { #GiftUserName3 {
animation: myScroll 9s 6s linear infinite; /* --- animation-duration = JS VAR --- */
animation-delay: 6s;
}
#GiftUserNameDupe1 {
/* --- animation-duration = JS VAR --- */
}
#GiftUserNameDupe2 {
/* --- animation-duration = JS VAR --- */
animation-delay: 3s;
}
#GiftUserNameDupe3 {
/* --- animation-duration = JS VAR --- */
animation-delay: 6s;
} }
.scrollWrap { .scrollWrap {
width: 100%; width: 100%;
mask-image: linear-gradient(to right, transparent 0%, black 5%, black 85%, transparent 100%); }
.fadeLeft {
mask-image: linear-gradient(to right, transparent 0%, black 1ch);
} }
#ScrollWrapGoal { #ScrollWrapGoal {
@@ -213,16 +267,22 @@ p {
.scrollWrapW { .scrollWrapW {
width: fit-content; width: fit-content;
display: flex;
} }
#ScrollWrapGoalW { #ScrollWrapGoalW {
} }
.hide {
display: none;
}
.scrollAnim {
animation: myScroll linear infinite;
}
@keyframes myScroll { @keyframes myScroll {
0% {left: 0%; top: 0%;} 0% {left: 0%; top: 0%;}
10% {left: 0%; top: 0%;} 42% {left: 0%; top: 0%;}
85% {left: -100%; top: 0%;} 100% {left: -54%; top: 0%;}
89.99% {left: -100%; top: 0%;}
90% {left: 0%; top: 100%;}
100% {left: 0%; top: 0%;}
} }

102
timer.js
View File

@@ -46,18 +46,21 @@ class Timer {
this.total_contribution, this.total_contribution,
this.next_goal.required, this.next_goal.required,
); );
this.renderer.animate();
} else if (this.last_goal != null) { } else if (this.last_goal != null) {
this.renderer.render_current_goal( this.renderer.render_current_goal(
this.last_goal.name, this.last_goal.name,
this.total_contribution, this.total_contribution,
this.last_goal.required, this.last_goal.required,
); );
this.renderer.animate();
} else { } else {
this.renderer.clear_current_goal(); this.renderer.clear_current_goal();
} }
// Render leaderboard // Render leaderboard
this.renderer.render_users(this.leaderboard); this.renderer.render_users(this.leaderboard);
this.renderer.animate();
// Render timer // Render timer
this.render_time(); this.render_time();
@@ -88,6 +91,7 @@ class TimerRenderer {
// Name of the goal to display // Name of the goal to display
this.goal_name = document.getElementById("GoalName") this.goal_name = document.getElementById("GoalName")
this.goal_namedupe = document.getElementById("GoalNameDupe")
// 1524/3000 Points // 1524/3000 Points
this.goal_progress = document.getElementById("GoalProgress") this.goal_progress = document.getElementById("GoalProgress")
@@ -96,14 +100,76 @@ class TimerRenderer {
this.topusers_label = document.getElementById("TopUsers") this.topusers_label = document.getElementById("TopUsers")
// Leaderboard items // Leaderboard items
// 1. (or points?) // 1. (or points?) (1 as in 1st place, static)
this.topusers_user1_num = document.getElementById("GiftUserNum1") this.topusers_user1_num = document.getElementById("GiftUserNum1")
// Name of user 1 // Name of user 1
this.topusers_user1_name = document.getElementById("GiftUserName1") this.topusers_user1_name = document.getElementById("GiftUserName1")
this.topusers_userdupe1_name = document.getElementById("GiftUserNameDupe1")
this.topusers_user2_num = document.getElementById("GiftUserNum2") this.topusers_user2_num = document.getElementById("GiftUserNum2")
this.topusers_user2_name = document.getElementById("GiftUserName2") this.topusers_user2_name = document.getElementById("GiftUserName2")
this.topusers_userdupe2_name = document.getElementById("GiftUserNameDupe2")
this.topusers_user3_num = document.getElementById("GiftUserNum3") this.topusers_user3_num = document.getElementById("GiftUserNum3")
this.topusers_user3_name = document.getElementById("GiftUserName3") this.topusers_user3_name = document.getElementById("GiftUserName3")
this.topusers_userdupe3_name = document.getElementById("GiftUserNameDupe3")
// -- Animation --
// Left-fade wrappers
this.scrollwraps = document.getElementsByClassName("scrollWrap")
// Parent wrappers
this.scrollboxes = [document.getElementById("GiftUserRow1"),
document.getElementById("GiftUserRow2"),
document.getElementById("GiftUserRow3"),
document.getElementById("ScrollWrapGoal")]
// Leaderboard names
this.userboxes = [this.topusers_user1_name, this.topusers_user2_name, this.topusers_user3_name]
this.userdupeboxes = [this.topusers_userdupe1_name, this.topusers_userdupe2_name, this.topusers_userdupe3_name]
}
// -- Toggles animation if text overflows container and adjusts speed for uniform movement --
animate() {
for (let i = 0; i < this.scrollboxes.length; i++) {
// Check whether the itteration is a username or goal
if (this.scrollboxes[i].id == 'ScrollWrapGoal') {
// Check if element text overflows
if (this.goal_name.getBoundingClientRect().width > (this.scrollboxes[i].getBoundingClientRect().width * .95) ) {
// Toggle animation
this.goal_name.classList.add("scrollAnim");
this.goal_namedupe.classList.add("scrollAnim");
this.goal_namedupe.style.display = 'inline-block';
this.scrollwraps[i].classList.add("fadeLeft");
// Set animation speed
this.goal_name.style.animationDuration = `${this.goal_name.getBoundingClientRect().width / 40}s`;
this.goal_namedupe.style.animationDuration = `${this.goal_name.getBoundingClientRect().width / 40}s`;
} else {
// Toggle animation
this.goal_name.classList.remove("scrollAnim");
this.goal_namedupe.classList.remove("scrollAnim");
this.goal_namedupe.style.display = 'none';
this.scrollwraps[i].classList.remove("fadeLeft");
}
} else {
// Check if element text overflows
if (this.userboxes[i].getBoundingClientRect().width > (this.scrollboxes[i].getBoundingClientRect().width * .8) ) {
// Toggle animation
this.userboxes[i].classList.add("scrollAnim");
this.userdupeboxes[i].classList.add("scrollAnim");
this.userdupeboxes[i].style.display = 'inline-block';
this.scrollwraps[i].classList.add("fadeLeft");
// Set animation speed
this.userboxes[i].style.animationDuration = `${this.userboxes[i].getBoundingClientRect().width / 40}s`;
this.userdupeboxes[i].style.animationDuration = `${this.userboxes[i].getBoundingClientRect().width / 40}s`;
} else {
// Toggle animation
this.userboxes[i].classList.remove("scrollAnim");
this.userdupeboxes[i].classList.remove("scrollAnim");
this.userdupeboxes[i].style.display = 'none';
this.scrollwraps[i].classList.remove("fadeLeft");
}
}
}
} }
/** /**
@@ -112,16 +178,20 @@ class TimerRenderer {
reset() { reset() {
this.timer.textContent = "00:00"; this.timer.textContent = "00:00";
this.goal_label.textContent = "Next Goal:"; this.goal_label.textContent = "Next Goal:";
this.goal_name.textContent = "Be awesome"; this.goal_name.textContent = "The answer was..";
this.goal_progress.textContent = "00/00"; this.goal_namedupe.textContent = "The answer was..";
this.goal_progress.textContent = "0000/0000";
this.topusers_label.textContent = "Leaderboard:"; this.topusers_label.textContent = "Leaderboard:";
this.topusers_user1_num.textContent = "1"; this.topusers_user1_num.textContent = "1";
this.topusers_user1_name.textContent = "Anonymous"; this.topusers_user1_name.textContent = "Username_Very_Super_Long12";
this.topusers_userdupe1_name.textContent = "Username_Very_Super_Long12";
this.topusers_user2_num.textContent = "2"; this.topusers_user2_num.textContent = "2";
this.topusers_user2_name.textContent = "Anonymous"; this.topusers_user2_name.textContent = "User";
this.topusers_userdupe2_name.textContent = "User";
this.topusers_user3_num.textContent = "3"; this.topusers_user3_num.textContent = "3";
this.topusers_user3_name.textContent = "Anonymous"; this.topusers_user3_name.textContent = "Username48";
this.topusers_userdupe3_name.textContent = "Username48";
} }
@@ -165,6 +235,8 @@ class TimerRenderer {
// Or after we accomplish all goals // Or after we accomplish all goals
this.goal_name.textContent = name; this.goal_name.textContent = name;
this.goal_namedupe.textContent = name;
//this.goal_name.style.animationDuration = `${this.goal_name.getBoundingClientRect().width / 50}s`;
// Bitwise OR done to convert floats to integers if needed // Bitwise OR done to convert floats to integers if needed
this.goal_progress.textContent = String(current | 0) + '/' + String(required | 0) this.goal_progress.textContent = String(current | 0) + '/' + String(required | 0)
@@ -175,6 +247,7 @@ class TimerRenderer {
*/ */
clear_current_goal() { clear_current_goal() {
this.goal_name.textContent = ""; this.goal_name.textContent = "";
//this.goal_namedupe.textContent = "";
this.goal_progress.textContent = ""; this.goal_progress.textContent = "";
this.goal_label.textContent = ""; this.goal_label.textContent = "";
} }
@@ -191,25 +264,34 @@ class TimerRenderer {
if (users.length >= 1) { if (users.length >= 1) {
this.topusers_user1_num.textContent = "1"; this.topusers_user1_num.textContent = "1";
this.topusers_user1_name.textContent = users[0].user_name; this.topusers_user1_name.textContent = users[0].user_name;
this.topusers_userdupe1_name.textContent = users[0].user_name;
//this.topusers_user1_name.style.animationDuration = `${this.topusers_user1_name.getBoundingClientRect().width / 50}s`;
} else { } else {
this.topusers_user1_num.textContent = ""; this.topusers_user1_num.textContent = "1";
this.topusers_user1_name.textContent = ""; this.topusers_user1_name.textContent = "";
this.topusers_userdupe1_name.textContent = "";
} }
if (users.length >= 2) { if (users.length >= 2) {
this.topusers_user2_num.textContent = "2"; this.topusers_user2_num.textContent = "2";
this.topusers_user2_name.textContent = users[1].user_name; this.topusers_user2_name.textContent = users[1].user_name;
this.topusers_userdupe2_name.textContent = users[1].user_name;
//this.topusers_user2_name.style.animationDuration = `${this.topusers_user2_name.getBoundingClientRect().width / 50}s`;
} else { } else {
this.topusers_user2_num.textContent = ""; this.topusers_user2_num.textContent = "2";
this.topusers_user2_name.textContent = ""; this.topusers_user2_name.textContent = "";
this.topusers_userdupe2_name.textContent = "";
} }
if (users.length >= 3) { if (users.length >= 3) {
this.topusers_user3_num.textContent = "3"; this.topusers_user3_num.textContent = "3";
this.topusers_user3_name.textContent = users[2].user_name; this.topusers_user3_name.textContent = users[2].user_name;
this.topusers_userdupe3_name.textContent = users[2].user_name;
//this.topusers_user3_name.style.animationDuration = `${this.topusers_user3_name.getBoundingClientRect().width / 50}s`;
} else { } else {
this.topusers_user3_num.textContent = ""; this.topusers_user3_num.textContent = "3";
this.topusers_user3_name.textContent = ""; this.topusers_user3_name.textContent = "";
this.topusers_userdupe3_name.textContent = "";
} }
} }
@@ -320,6 +402,7 @@ function communicate(websocket) {
) )
); );
renderer.reset(); renderer.reset();
renderer.animate();
}; };
websocket.onmessage = ({ data }) => { websocket.onmessage = ({ data }) => {
@@ -345,6 +428,7 @@ function communicate(websocket) {
timer = null; timer = null;
} }
renderer.reset(); renderer.reset();
renderer.animate();
case "endTimer": case "endTimer":
if (timer != null) { if (timer != null) {
timer.destroying = true; timer.destroying = true;