Example: Goal Banner¶
The goal-banner overlay shows an animated banner at the top of the screen when a goal is scored. The banner displays the scorer's name and uses a color bar to indicate which team scored. It disappears automatically after 4 seconds.
This example demonstrates event-triggered animations and cleanup logic for rapid events.
For short recipes covering replay, statfeed, session, and other simple events, see Event Snippets.
File structure¶
script.js¶
const banner = document.getElementById("banner");
const scorerEl = document.getElementById("scorer-name");
const teamBar = document.getElementById("team-bar");
let hideTimer = null;
function showGoal(goal) {
if (hideTimer) {
clearTimeout(hideTimer);
hideTimer = null;
}
scorerEl.textContent = goal.player_name || "Goal!";
teamBar.className = `team-bar ${goal.team || ""}`;
banner.classList.remove("hide", "show");
void banner.offsetWidth;
banner.classList.add("show");
hideTimer = setTimeout(() => {
banner.classList.remove("show");
banner.classList.add("hide");
}, 4000);
}
RLOverlay.on("goal:scored", showGoal);
How showGoal works¶
Every call to showGoal follows the same steps:
-
Cancel any pending hide timer. If two goals are scored within 4 seconds, the banner stays visible and restarts from the new goal rather than disappearing mid-display.
-
Write the scorer's name. The
|| "Goal!"fallback handles the case whereplayer_nameis empty or undefined. -
Set the team color bar. The
teamBar.classNameassignment replaces all classes on the element withteam-bar blueorteam-bar orange. CSS rules instyle.cssuse these classes to set the bar color. -
Reset and restart the animation. Removing both
showandhideclasses clears the animation state.void banner.offsetWidthforces a layout reflow so the browser registers the class removal before the new class is added. Addingshowstarts the entry animation. -
Schedule the hide. After 4000 milliseconds, the
showclass is replaced withhide, which triggers the exit animation. The timer reference is saved inhideTimerso it can be cancelled if a new goal arrives before it fires.
The animation CSS¶
.banner.show {
animation: bannerIn 0.4s cubic-bezier(0.22, 1, 0.36, 1) forwards;
}
.banner.hide {
animation: bannerOut 0.35s ease-in forwards;
}
@keyframes bannerIn {
from { opacity: 0; transform: translateY(-60px) scale(0.7); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes bannerOut {
from { opacity: 1; transform: scale(1); }
to { opacity: 0; transform: scale(1.08); }
}
bannerIn slides the banner down from above while scaling it up. The cubic-bezier(0.22, 1, 0.36, 1) easing produces a springy overshoot feel. bannerOut fades it out while scaling it up slightly, giving the impression it pops away.
The forwards fill mode keeps the final animation frame applied after the animation ends. Without it, the banner would snap back to its initial state when the animation completes.
What to learn from this example¶
- Store the timeout reference in a variable and cancel it at the start of each new event. Without this, overlapping events produce unpredictable behavior.
- The
|| "fallback"pattern on string fields prevents blank or broken UI when data is missing. - Replacing
classNameentirely is the simplest way to switch between a fixed set of CSS state classes. - The reflow trick (
void el.offsetWidth) is required any time you need to restart an animation by toggling classes. - The overlay only listens to one event (
goal:scored) and ignores everything else. Keep overlays focused on the data they actually use.