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>
<main>
<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='infoGroup'>
<div class='infoBox' id='InfoBox3'>
<p id='TopUsers'>
Leaderboard:
</p>
<div class='giftUserWrap'>
<div class='giftUserRow' id='GiftUserRow1'>
<p class='giftUserNum' id='GiftUserNum1'>
1
</p>
<div class='scrollWrap' id='ScrollWrapName1'>
<div class='scrollWrapW' id='ScrollWrapNameW1'>
<p class='giftUserName'id='GiftUserName1'>
Usernamethatisverylong
</p>
<div class='rightFadeWrap' id='RightFadeWrapInfoBox3'>
<div class='giftUserWrap' id='GiftUserWrap'>
<div class='giftUserRow' id='GiftUserRow1'>
<p class='giftUserNum' id='GiftUserNum1'>
1
</p>
<div class='scrollWrap' id='ScrollWrapName1'>
<div class='scrollWrapW' id='ScrollWrapNameW1'>
<p class='giftUserName'id='GiftUserName1'>
Usernamethatisverylong
</p>
<p class='giftUserNameDupe'id='GiftUserNameDupe1'>
Usernamethatisverylong
</p>
</div>
</div>
</div>
</div>
<div class='giftUserRow' id='GiftUserRow2'>
<p class='giftUserNum' id='GiftUserNum2'>
2
</p>
<div class='scrollWrap' id='ScrollWrapName2'>
<div class='scrollWrapW' id='ScrollWrapNameW2'>
<p class='giftUserName'id='GiftUserName2'>
Usernamethatisverylong
</p>
<div class='giftUserRow' id='GiftUserRow2'>
<p class='giftUserNum' id='GiftUserNum2'>
2
</p>
<div class='scrollWrap' id='ScrollWrapName2'>
<div class='scrollWrapW' id='ScrollWrapNameW2'>
<p class='giftUserName'id='GiftUserName2'>
Usernamethatisverylong
</p>
<p class='giftUserNameDupe'id='GiftUserNameDupe2'>
Usernamethatisverylong
</p>
</div>
</div>
</div>
</div>
<div class='giftUserRow' id='GiftUserRow3'>
<p class='giftUserNum' id='GiftUserNum3'>
3
</p>
<div class='scrollWrap' id='ScrollWrapName3'>
<div class='scrollWrapW' id='ScrollWrapNameW3'>
<p class='giftUserName'id='GiftUserName3'>
Usernamethatisverylong
</p>
<div class='giftUserRow' id='GiftUserRow3'>
<p class='giftUserNum' id='GiftUserNum3'>
3
</p>
<div class='scrollWrap' id='ScrollWrapName3'>
<div class='scrollWrapW' id='ScrollWrapNameW3'>
<p class='giftUserName'id='GiftUserName3'>
Usernamethatisverylong
</p>
<p class='giftUserNameDupe'id='GiftUserNameDupe3'>
Usernamethatisverylong
</p>
</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'>
<h1 id='Timer'>
00:00
@@ -68,15 +85,23 @@
!subathon for details
</p>
</div>
<div class='infoBox' id='InfoBox2'>
<p id='GoalLabel'>
Next Goal:
</p>
<div class='scrollWrap' id='ScrollWrapGoal'>
<div class='scrollWrapW' id='ScrollWrapGoalW'>
<p id='GoalName'>
Discord Watch Party + Puzzles
</p>
<div class='rightFadeWrap' id='RightFadeWrapInfoBox2'>
<div class='scrollWrap' id='ScrollWrapGoal'>
<div class='scrollWrapW' id='ScrollWrapGoalW'>
<p id='GoalName'>
Discord Watch Party + Puzzles
</p>
<p id='GoalNameDupe'>
Discord Watch Party + Puzzles
</p>
</div>
</div>
</div>
<div id='GoalProgressWrap'>
@@ -85,6 +110,7 @@
</p>
</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 {
}
#InfoBox2 {
@@ -116,6 +117,13 @@ p {
border: .4cqh solid;
border-color: #00A0F3;
background-color: white;
}
.rightFadeWrap {
mask-image: linear-gradient(to right, black 85%, transparent 100%);
}
#RightFadeWrapInfoBox3 {
}
@@ -140,7 +148,20 @@ p {
position: relative;
color: #32B993;
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 {
@@ -161,7 +182,6 @@ p {
#TopUsers {
margin-bottom: 2%;
transform: translate(0%, 0%) scale(1);
font-weight: normal;
color: #404145;
}
@@ -187,21 +207,55 @@ p {
position: relative;
color: #32B993;
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 {
animation: myScroll 9s linear infinite;
/* --- animation-duration = JS VAR --- */
}
#GiftUserName2 {
animation: myScroll 9s 3s linear infinite;
/* --- animation-duration = JS VAR --- */
animation-delay: 3s;
}
#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 {
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 {
@@ -213,16 +267,22 @@ p {
.scrollWrapW {
width: fit-content;
display: flex;
}
#ScrollWrapGoalW {
}
.hide {
display: none;
}
.scrollAnim {
animation: myScroll linear infinite;
}
@keyframes myScroll {
0% {left: 0%; top: 0%;}
10% {left: 0%; top: 0%;}
85% {left: -100%; top: 0%;}
89.99% {left: -100%; top: 0%;}
90% {left: 0%; top: 100%;}
100% {left: 0%; top: 0%;}
42% {left: 0%; top: 0%;}
100% {left: -54%; top: 0%;}
}

102
timer.js
View File

@@ -46,18 +46,21 @@ class Timer {
this.total_contribution,
this.next_goal.required,
);
this.renderer.animate();
} else if (this.last_goal != null) {
this.renderer.render_current_goal(
this.last_goal.name,
this.total_contribution,
this.last_goal.required,
);
this.renderer.animate();
} else {
this.renderer.clear_current_goal();
}
// Render leaderboard
this.renderer.render_users(this.leaderboard);
this.renderer.animate();
// Render timer
this.render_time();
@@ -88,6 +91,7 @@ class TimerRenderer {
// Name of the goal to display
this.goal_name = document.getElementById("GoalName")
this.goal_namedupe = document.getElementById("GoalNameDupe")
// 1524/3000 Points
this.goal_progress = document.getElementById("GoalProgress")
@@ -96,14 +100,76 @@ class TimerRenderer {
this.topusers_label = document.getElementById("TopUsers")
// Leaderboard items
// 1. (or points?)
// 1. (or points?) (1 as in 1st place, static)
this.topusers_user1_num = document.getElementById("GiftUserNum1")
// Name of user 1
this.topusers_user1_name = document.getElementById("GiftUserName1")
this.topusers_userdupe1_name = document.getElementById("GiftUserNameDupe1")
this.topusers_user2_num = document.getElementById("GiftUserNum2")
this.topusers_user2_name = document.getElementById("GiftUserName2")
this.topusers_userdupe2_name = document.getElementById("GiftUserNameDupe2")
this.topusers_user3_num = document.getElementById("GiftUserNum3")
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() {
this.timer.textContent = "00:00";
this.goal_label.textContent = "Next Goal:";
this.goal_name.textContent = "Be awesome";
this.goal_progress.textContent = "00/00";
this.goal_name.textContent = "The answer was..";
this.goal_namedupe.textContent = "The answer was..";
this.goal_progress.textContent = "0000/0000";
this.topusers_label.textContent = "Leaderboard:";
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_name.textContent = "Anonymous";
this.topusers_user2_name.textContent = "User";
this.topusers_userdupe2_name.textContent = "User";
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
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
this.goal_progress.textContent = String(current | 0) + '/' + String(required | 0)
@@ -175,6 +247,7 @@ class TimerRenderer {
*/
clear_current_goal() {
this.goal_name.textContent = "";
//this.goal_namedupe.textContent = "";
this.goal_progress.textContent = "";
this.goal_label.textContent = "";
}
@@ -191,25 +264,34 @@ class TimerRenderer {
if (users.length >= 1) {
this.topusers_user1_num.textContent = "1";
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 {
this.topusers_user1_num.textContent = "";
this.topusers_user1_num.textContent = "1";
this.topusers_user1_name.textContent = "";
this.topusers_userdupe1_name.textContent = "";
}
if (users.length >= 2) {
this.topusers_user2_num.textContent = "2";
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 {
this.topusers_user2_num.textContent = "";
this.topusers_user2_num.textContent = "2";
this.topusers_user2_name.textContent = "";
this.topusers_userdupe2_name.textContent = "";
}
if (users.length >= 3) {
this.topusers_user3_num.textContent = "3";
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 {
this.topusers_user3_num.textContent = "";
this.topusers_user3_num.textContent = "3";
this.topusers_user3_name.textContent = "";
this.topusers_userdupe3_name.textContent = "";
}
}
@@ -320,6 +402,7 @@ function communicate(websocket) {
)
);
renderer.reset();
renderer.animate();
};
websocket.onmessage = ({ data }) => {
@@ -345,6 +428,7 @@ function communicate(websocket) {
timer = null;
}
renderer.reset();
renderer.animate();
case "endTimer":
if (timer != null) {
timer.destroying = true;