diff --git a/src/graphics/scoreboard-2xko/main.vue b/src/graphics/scoreboard-2xko/main.vue index 7b81d3e..804e078 100644 --- a/src/graphics/scoreboard-2xko/main.vue +++ b/src/graphics/scoreboard-2xko/main.vue @@ -1,6 +1,6 @@ @@ -216,93 +278,105 @@ const gameText = computed(() => scoreboard.value.game || '2XKO'); - + + + - + + + @@ -313,12 +387,27 @@ const gameText = computed(() => scoreboard.value.game || '2XKO'); class="nameBlock" > - {{ leftTeam }} | {{ leftName }} + + + {{ leftTeam }} | {{ leftName }} + + - - {{ scoreboard.leftScore }} + + + {{ scoreboard.leftScore }} + @@ -327,12 +416,22 @@ const gameText = computed(() => scoreboard.value.game || '2XKO'); id="stageText" class="infoBar" > - {{ gameText }} + + {{ gameText }} + - {{ roundText }} + + {{ roundText }} + scoreboard.value.game || '2XKO'); - - {{ scoreboard.rightScore }} + + + {{ scoreboard.rightScore }} + scoreboard.value.game || '2XKO'); class="nameBlock" > - {{ rightTeam }} | {{ rightName }} + + + {{ rightTeam }} | {{ rightName }} + + @@ -641,6 +755,64 @@ body { max-width: 46px; } +.text-fade-enter-active, +.text-fade-leave-active { + transition: opacity 180ms ease, transform 180ms ease; +} + +.text-fade-enter-from, +.text-fade-leave-to { + opacity: 0; + transform: translateY(6px); +} + +.score-flip-enter-active, +.score-flip-leave-active { + transition: opacity 200ms ease, transform 200ms ease; + display: inline-block; +} + +.score-flip-enter-from { + opacity: 0; + transform: translateY(-10px) scale(0.95); +} + +.score-flip-leave-to { + opacity: 0; + transform: translateY(10px) scale(0.95); +} + +.score-up { + animation: score-up-pulse 420ms ease; +} + +.score-down { + animation: score-down-pulse 420ms ease; +} + +.flag-swap-enter-active, +.flag-swap-leave-active { + transition: opacity 220ms ease, transform 220ms ease; +} + +.flag-swap-enter-from, +.flag-swap-leave-to { + opacity: 0; + transform: scale(0.95); +} + +@keyframes score-up-pulse { + 0% { transform: scale(1); color: #ffffff; } + 50% { transform: scale(1.18); color: #a7ffcf; } + 100% { transform: scale(1); color: #ffffff; } +} + +@keyframes score-down-pulse { + 0% { transform: scale(1); color: #ffffff; } + 50% { transform: scale(1.18); color: #ffd0d0; } + 100% { transform: scale(1); color: #ffffff; } +} + .team-custom-image-style { max-height: 29px; max-width: 39px; diff --git a/src/graphics/scoreboard/main.vue b/src/graphics/scoreboard/main.vue index 1fba7cd..bffbc52 100644 --- a/src/graphics/scoreboard/main.vue +++ b/src/graphics/scoreboard/main.vue @@ -110,6 +110,59 @@ watch( const roundText = computed(() => scoreboard.value.round || 'Round'); +const leftScoreAnimClass = ref(''); +const rightScoreAnimClass = ref(''); +let leftScoreTimer: ReturnType | undefined; +let rightScoreTimer: ReturnType | undefined; + +const triggerScoreAnimation = (side: 'left' | 'right', direction: 'up' | 'down') => { + const className = direction === 'up' ? 'score-up' : 'score-down'; + if (side === 'left') { + leftScoreAnimClass.value = ''; + if (leftScoreTimer) { + clearTimeout(leftScoreTimer); + } + void nextTick(() => { + leftScoreAnimClass.value = className; + leftScoreTimer = setTimeout(() => { + leftScoreAnimClass.value = ''; + }, 420); + }); + return; + } + + rightScoreAnimClass.value = ''; + if (rightScoreTimer) { + clearTimeout(rightScoreTimer); + } + void nextTick(() => { + rightScoreAnimClass.value = className; + rightScoreTimer = setTimeout(() => { + rightScoreAnimClass.value = ''; + }, 420); + }); +}; + +watch( + () => scoreboard.value.leftScore, + (nextScore, previousScore) => { + if (previousScore === undefined || nextScore === previousScore) { + return; + } + triggerScoreAnimation('left', nextScore > previousScore ? 'up' : 'down'); + }, +); + +watch( + () => scoreboard.value.rightScore, + (nextScore, previousScore) => { + if (previousScore === undefined || nextScore === previousScore) { + return; + } + triggerScoreAnimation('right', nextScore > previousScore ? 'up' : 'down'); + }, +); + const progressTextWrapperRef = ref(null); const progressTextRef = ref(null); const p1NameTextWrapperRef = ref(null); @@ -162,6 +215,12 @@ onMounted(() => { onBeforeUnmount(() => { window.removeEventListener('resize', refitText); + if (leftScoreTimer) { + clearTimeout(leftScoreTimer); + } + if (rightScoreTimer) { + clearTimeout(rightScoreTimer); + } }); watch( @@ -206,7 +265,12 @@ watch( id="progress-text" ref="progressTextRef" > - {{ roundText }} + + {{ roundText }} + @@ -218,8 +282,14 @@ watch( - {{ scoreboard.leftScore }} + + {{ scoreboard.leftScore }} + @@ -231,8 +301,14 @@ watch( - {{ scoreboard.rightScore }} + + {{ scoreboard.rightScore }} + @@ -251,31 +327,41 @@ watch( class="name-text-wrapper" > - - {{ leftTeam }} - - - {{ leftName }} - + + + {{ leftTeam }} + + + {{ leftName }} + + + - - - + + + + + - + - - {{ rightTeam }} - - - {{ rightName }} - + + + {{ rightTeam }} + + + {{ rightName }} + + + - - - + + + + + - + @@ -535,4 +631,62 @@ img { .gamertag-text { color: #ffffff; } + +.text-fade-enter-active, +.text-fade-leave-active { + transition: opacity 180ms ease, transform 180ms ease; +} + +.text-fade-enter-from, +.text-fade-leave-to { + opacity: 0; + transform: translateY(6px); +} + +.score-flip-enter-active, +.score-flip-leave-active { + transition: opacity 200ms ease, transform 200ms ease; + display: inline-block; +} + +.score-flip-enter-from { + opacity: 0; + transform: translateY(-10px) scale(0.95); +} + +.score-flip-leave-to { + opacity: 0; + transform: translateY(10px) scale(0.95); +} + +.score-up { + animation: score-up-pulse 420ms ease; +} + +.score-down { + animation: score-down-pulse 420ms ease; +} + +.flag-swap-enter-active, +.flag-swap-leave-active { + transition: opacity 200ms ease, transform 200ms ease; +} + +.flag-swap-enter-from, +.flag-swap-leave-to { + opacity: 0; + transform: scale(0.92); +} + +@keyframes score-up-pulse { + 0% { transform: scale(1); color: #ffffff; } + 50% { transform: scale(1.2); color: #a7ffcf; } + 100% { transform: scale(1); color: #ffffff; } +} + +@keyframes score-down-pulse { + 0% { transform: scale(1); color: #ffffff; } + 50% { transform: scale(1.2); color: #ffd0d0; } + 100% { transform: scale(1); color: #ffffff; } +}