From 5159f6269350cb5d5f641e3f2d7f2b92e727deb1 Mon Sep 17 00:00:00 2001 From: Maier Date: Fri, 3 Jan 2020 16:32:03 -0500 Subject: [PATCH 1/5] base animation --- server.js | 3 +- static/game.js | 7 ++- static/styles.css | 114 ++++++++++++++++++++++++++++++++++++++++++++++ views/game.html | 6 +++ 4 files changed, 128 insertions(+), 2 deletions(-) diff --git a/server.js b/server.js index 2b9187d..fd37327 100644 --- a/server.js +++ b/server.js @@ -169,7 +169,8 @@ io.on('connection', function(socket) { if (game) { let player = game.players.find((player) => player.id === id); game.players.find((player) => player.id === id).dead = true; - game.message = player.name + ", a " + player.card.role + ", has been killed!"; + game.killedPlayer = player; + game.killedRole = player.card.role; const winCheck = teamWon(game); if (winCheck === "wolf") { game.winningTeam = "wolf"; diff --git a/static/game.js b/static/game.js index ed87fbb..7a349a0 100644 --- a/static/game.js +++ b/static/game.js @@ -11,7 +11,8 @@ let cardRendered = false; // respond to the game state received from the server socket.on('state', function(game) { currentGame = game; - if (game.message) { + if (game.killedPlayer && game.killedRole) { + playKilledAnimation(); document.getElementById("message-box").innerText = game.message; } buildGameBasedOnState(); @@ -33,6 +34,10 @@ function buildGameBasedOnState() { } } +function playKilledAnimation() { + +} + function launchGame() { randomlyDealCardsToPlayers(); socket.emit('startGame', { players: currentGame.players , code: currentGame.accessCode}); diff --git a/static/styles.css b/static/styles.css index c347409..b85c6b5 100644 --- a/static/styles.css +++ b/static/styles.css @@ -220,6 +220,55 @@ html, body { } } +@keyframes slide-fade-top { + 0% { + opacity: 0; + transform: translateY(-150px) + + } + 50% { + opacity: 1; + transform: translateY(0); + } + + 100% { + opacity: 0; + transform: translateY(-150px) + + } +} + +@keyframes slide-fade-bottom { + 0% { + opacity: 0; + transform: translateY(150px) + + } + 50% { + opacity: 1; + transform: translateY(0); + } + + 100% { + opacity: 0; + transform: translateY(150px) + + } +} + +@keyframes fade-overlay { + 0% { + background-color: rgba(0,0,0,0.0); + } + 50% { + background-color: rgba(0,0,0,0.9); + } + + 100% { + background-color: rgba(0,0,0,0.0); + } +} + #app-content { text-align: center; margin: 0 auto; @@ -699,6 +748,17 @@ label { } } +@keyframes flip-slide { + 0% { + transform: rotateY(0deg); + transform: translateX(-100px); + } + 100% { + transform: rotateY(-90deg); + transform: translateX(0px); + } +} + @keyframes flip-down { 0% { transform: rotateY(0deg); @@ -756,6 +816,12 @@ label { animation-fill-mode: forwards; } +.flip-slide{ + animation: flip-slide 1s; + animation-fill-mode: forwards; +} + + #game-card h2 { font-size: 1.7em; font-family: 'diavlo', sans-serif; @@ -1015,6 +1081,54 @@ label { color: #7d0b0b; } +.overlay { + position: fixed; + width: 100%; + height: 100%; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0,0,0,0.9); + z-index: 3; + animation: fade-overlay 5s; + animation-timing-function: cubic-bezier(0.39, 0.575, 0.565, 1); +} + +#killed-name { + color: white; + padding-top: 2em; + font-size: 2.5em; + margin: 0; + animation: slide-fade-top 5s; + animation-timing-function: cubic-bezier(0.39, 0.575, 0.565, 1); +} + +#killed-role { + animation: slide-fade-bottom 5s; + animation-timing-function: cubic-bezier(0.39, 0.575, 0.565, 1); +} + + +.killed-card { + width: 50px; + height: 200px; + background-color: white; + position: absolute; + top: 19%; + left: 14%; + background-color: transparent; + cursor: pointer; + justify-content: space-between; + max-width: 17em; + border-radius: 3px; + height: 23em; + width: 72%; + box-shadow: 0 13px 17px rgba(0,0,0,0.6); + perspective: 1000px; + transform-style: preserve-3d; +} + @keyframes animatetop { from {top: -300px; opacity: 0} to {top: 0; opacity: 1} diff --git a/views/game.html b/views/game.html index 0fe352f..0c801a2 100644 --- a/views/game.html +++ b/views/game.html @@ -9,6 +9,12 @@
+
+
+

Test was a role!

+ Werewolf +
+
From 37dbade6a47bdcc50a931d38b2a1f7b30be9012c Mon Sep 17 00:00:00 2001 From: Maier Date: Tue, 7 Jan 2020 16:38:35 -0500 Subject: [PATCH 2/5] mostly working killed animation --- server.js | 4 +- static/game.js | 38 ++++++++- static/styles.css | 196 +++++++++++++++++++++++++++++----------------- views/game.html | 8 +- 4 files changed, 165 insertions(+), 81 deletions(-) diff --git a/server.js b/server.js index fd37327..3147929 100644 --- a/server.js +++ b/server.js @@ -169,8 +169,10 @@ io.on('connection', function(socket) { if (game) { let player = game.players.find((player) => player.id === id); game.players.find((player) => player.id === id).dead = true; - game.killedPlayer = player; + game.killedPlayer = player.name; + game.lastKilled = player.id; game.killedRole = player.card.role; + game.message = player.name + ", a " + player.card.role + " was killed!" const winCheck = teamWon(game); if (winCheck === "wolf") { game.winningTeam = "wolf"; diff --git a/static/game.js b/static/game.js index 7a349a0..86fc933 100644 --- a/static/game.js +++ b/static/game.js @@ -7,11 +7,13 @@ let clock; let currentGame = null; let cardFlippedOver = false; let cardRendered = false; +let lastKilled = null; // respond to the game state received from the server socket.on('state', function(game) { currentGame = game; - if (game.killedPlayer && game.killedRole) { + if (game.killedPlayer && game.killedRole && game.lastKilled !== lastKilled) { // a new player has been killed + lastKilled = game.lastKilled; playKilledAnimation(); document.getElementById("message-box").innerText = game.message; } @@ -34,8 +36,40 @@ function buildGameBasedOnState() { } } +function hideAfterExit(e) { + e.target.style.display = 'none' + e.target.removeEventListener('animationend', triggerExitAnimation); + e.target.removeEventListener('animationend', hideAfterExit); +} + +function triggerExitAnimation(e) { + e.target.classList.remove(e.target.entranceClass); + e.target.classList.remove(e.target.exitClass); + e.target.offsetWidth; + e.target.classList.add(e.target.exitClass); + window.setTimeout(()=>{ + e.target.addEventListener('animationend', hideAfterExit); + },0); +} + +function triggerEntranceAnimation(selector, entranceClass, exitClass, image) { + let transitionEl = document.querySelector(selector); + transitionEl.style.display = 'block'; + transitionEl.addEventListener('animationend', triggerExitAnimation); + transitionEl.classList.remove(entranceClass); + transitionEl.entranceClass = entranceClass; + transitionEl.exitClass = exitClass; + transitionEl.offsetWidth; + if (image) { + transitionEl.setAttribute("src", "../assets/images/roles/" + currentGame.killedRole + ".png"); + } + transitionEl.classList.add(entranceClass); +} + function playKilledAnimation() { - + triggerEntranceAnimation('#overlay', 'animate-overlay-in', 'animate-overlay-out', false); + triggerEntranceAnimation('#killed-role', 'animate-role-in', 'animate-role-out', true); + triggerEntranceAnimation('#killed-name', 'animate-name-in', 'animate-name-out', false); } function launchGame() { diff --git a/static/styles.css b/static/styles.css index b85c6b5..32fcd4e 100644 --- a/static/styles.css +++ b/static/styles.css @@ -220,55 +220,6 @@ html, body { } } -@keyframes slide-fade-top { - 0% { - opacity: 0; - transform: translateY(-150px) - - } - 50% { - opacity: 1; - transform: translateY(0); - } - - 100% { - opacity: 0; - transform: translateY(-150px) - - } -} - -@keyframes slide-fade-bottom { - 0% { - opacity: 0; - transform: translateY(150px) - - } - 50% { - opacity: 1; - transform: translateY(0); - } - - 100% { - opacity: 0; - transform: translateY(150px) - - } -} - -@keyframes fade-overlay { - 0% { - background-color: rgba(0,0,0,0.0); - } - 50% { - background-color: rgba(0,0,0,0.9); - } - - 100% { - background-color: rgba(0,0,0,0.0); - } -} - #app-content { text-align: center; margin: 0 auto; @@ -1081,8 +1032,10 @@ label { color: #7d0b0b; } -.overlay { +#overlay { position: fixed; + user-select: none; + display: none; width: 100%; height: 100%; top: 0; @@ -1091,42 +1044,139 @@ label { bottom: 0; background-color: rgba(0,0,0,0.9); z-index: 3; - animation: fade-overlay 5s; - animation-timing-function: cubic-bezier(0.39, 0.575, 0.565, 1); } #killed-name { + display: none; color: white; padding-top: 2em; font-size: 2.5em; margin: 0; - animation: slide-fade-top 5s; - animation-timing-function: cubic-bezier(0.39, 0.575, 0.565, 1); } #killed-role { - animation: slide-fade-bottom 5s; - animation-timing-function: cubic-bezier(0.39, 0.575, 0.565, 1); + display: none; + margin: 0 auto; } +@keyframes slide-fade-in-top { + 0% { + opacity: 0; + transform: translateY(-150px) -.killed-card { - width: 50px; - height: 200px; - background-color: white; - position: absolute; - top: 19%; - left: 14%; - background-color: transparent; - cursor: pointer; - justify-content: space-between; - max-width: 17em; - border-radius: 3px; - height: 23em; - width: 72%; - box-shadow: 0 13px 17px rgba(0,0,0,0.6); - perspective: 1000px; - transform-style: preserve-3d; + } + 100% { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes slide-fade-out-top { + 0% { + opacity: 1; + transform: translateY(0px) + + } + 100% { + opacity: 0; + transform: translateY(-150px); + } +} + +@keyframes slide-fade-in-bottom { + 0% { + opacity: 0; + transform: translateY(150px) + + } + 100% { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes slide-fade-out-bottom { + 0% { + opacity: 1; + transform: translateY(0); + } + 100% { + opacity: 0; + transform: translateY(150px) + + } +} + +@keyframes fade-overlay-in { + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +@keyframes fade-overlay-out { + 0% { + opacity: 1; + } + 100% { + opacity: 0; + } +} + +.animate-overlay-in { + animation: fade-overlay-in 5s; + animation-timing-function: cubic-bezier(0.19, 1, 0.22, 1); + animation-fill-mode: forwards; + -webkit-animation: fade-overlay-in 5s; + -webkit-animation-timing-function: cubic-bezier(0.19, 1, 0.22, 1); + -webkit-animation-fill-mode: forwards; +} + +.animate-overlay-out { + animation: fade-overlay-out 1s; + animation-timing-function: cubic-bezier(0.95, 0.05, 0.795, 0.035); + animation-fill-mode: forwards; + -webkit-animation: fade-overlay-out 1s; + -webkit-animation-timing-function: cubic-bezier(0.95, 0.05, 0.795, 0.035); + -webkit-animation-fill-mode: forwards; +} + +.animate-name-in { + animation: slide-fade-in-top 5s; + animation-timing-function: cubic-bezier(0.19, 1, 0.22, 1); + animation-fill-mode: forwards; + -webkit-animation: slide-fade-in-top 5s; + -webkit-animation-timing-function: cubic-bezier(0.19, 1, 0.22, 1); + -webkit-animation-fill-mode: forwards; +} + +.animate-name-out { + animation: slide-fade-out-top 1s; + animation-timing-function: cubic-bezier(0.95, 0.05, 0.795, 0.035); + animation-fill-mode: forwards; + -webkit-animation: slide-fade-out-top 1s; + -webkit-animation-timing-function: cubic-bezier(0.95, 0.05, 0.795, 0.035); + -webkit-animation-fill-mode: forwards; +} + +.animate-role-in { + animation: slide-fade-in-bottom 5s; + animation-timing-function: cubic-bezier(0.19, 1, 0.22, 1); + animation-fill-mode: forwards; + -webkit-animation: slide-fade-in-bottom 5s; + -webkit-animation-timing-function: cubic-bezier(0.19, 1, 0.22, 1); + -webkit-animation-fill-mode: forwards; +} + +.animate-role-out { + animation: slide-fade-out-bottom 1s; + animation-timing-function: cubic-bezier(0.95, 0.05, 0.795, 0.035); + animation-fill-mode: forwards; + -webkit-animation: slide-fade-out-bottom 1s; + -webkit-animation-timing-function: cubic-bezier(0.95, 0.05, 0.795, 0.035); + -webkit-animation-fill-mode: forwards; } @keyframes animatetop { diff --git a/views/game.html b/views/game.html index 0c801a2..0bffed2 100644 --- a/views/game.html +++ b/views/game.html @@ -9,11 +9,9 @@
-
-
-

Test was a role!

- Werewolf -
+
+

Test was a role!

+
From 051ba932fec8a5b378f8e7356f266855c91fb602 Mon Sep 17 00:00:00 2001 From: Maier Date: Wed, 8 Jan 2020 11:17:35 -0500 Subject: [PATCH 3/5] Stable animation, pause when window not focused --- server.js | 2 +- static/game.js | 30 +++++++++++++++++++++--------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/server.js b/server.js index 3147929..bd725d2 100644 --- a/server.js +++ b/server.js @@ -172,7 +172,7 @@ io.on('connection', function(socket) { game.killedPlayer = player.name; game.lastKilled = player.id; game.killedRole = player.card.role; - game.message = player.name + ", a " + player.card.role + " was killed!" + game.message = player.name + ", a " + player.card.role + ", was killed!" const winCheck = teamWon(game); if (winCheck === "wolf") { game.winningTeam = "wolf"; diff --git a/static/game.js b/static/game.js index 86fc933..452f6ac 100644 --- a/static/game.js +++ b/static/game.js @@ -12,14 +12,21 @@ let lastKilled = null; // respond to the game state received from the server socket.on('state', function(game) { currentGame = game; - if (game.killedPlayer && game.killedRole && game.lastKilled !== lastKilled) { // a new player has been killed - lastKilled = game.lastKilled; - playKilledAnimation(); - document.getElementById("message-box").innerText = game.message; - } buildGameBasedOnState(); }); +window.onblur = function() { // pause animations if the window is not in focus + this.document.querySelector("#overlay").style.animationPlayState = 'paused'; + this.document.querySelector("#killed-role").style.animationPlayState = 'paused'; + this.document.querySelector("#killed-name").style.animationPlayState = 'paused'; +} + +window.onfocus = function() { // play animations when window is focused + this.document.querySelector("#overlay").style.animationPlayState = 'running'; + this.document.querySelector("#killed-role").style.animationPlayState = 'running'; + this.document.querySelector("#killed-name").style.animationPlayState = 'running'; +} + function buildGameBasedOnState() { switch(currentGame.state) { case "lobby": @@ -38,8 +45,7 @@ function buildGameBasedOnState() { function hideAfterExit(e) { e.target.style.display = 'none' - e.target.removeEventListener('animationend', triggerExitAnimation); - e.target.removeEventListener('animationend', hideAfterExit); + e.target.classList.remove(e.target.exitClass); } function triggerExitAnimation(e) { @@ -48,14 +54,14 @@ function triggerExitAnimation(e) { e.target.offsetWidth; e.target.classList.add(e.target.exitClass); window.setTimeout(()=>{ - e.target.addEventListener('animationend', hideAfterExit); + e.target.addEventListener('animationend', hideAfterExit, {"capture": true, "once": true}); },0); } function triggerEntranceAnimation(selector, entranceClass, exitClass, image) { let transitionEl = document.querySelector(selector); transitionEl.style.display = 'block'; - transitionEl.addEventListener('animationend', triggerExitAnimation); + transitionEl.addEventListener('animationend', triggerExitAnimation, {"capture": true, "once": true}); transitionEl.classList.remove(entranceClass); transitionEl.entranceClass = entranceClass; transitionEl.exitClass = exitClass; @@ -118,6 +124,12 @@ function renderEndSplash() { } function renderGame() { + if (currentGame.killedRole && currentGame.lastKilled !== lastKilled) { // a new player has been killed + lastKilled = currentGame.lastKilled; + document.getElementById("killed-name").innerText = currentGame.killedPlayer + " was a " + currentGame.killedRole + "!"; + playKilledAnimation(); + document.getElementById("message-box").innerText = currentGame.message; + } const player = currentGame.players.find((player) => player.id === sessionStorage.getItem("id")); // render the header From f52972d6f4f907ac243a4d40c2c8184be1a945aa Mon Sep 17 00:00:00 2001 From: Maier Date: Wed, 8 Jan 2020 11:18:31 -0500 Subject: [PATCH 4/5] removed placeholder text --- views/game.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/game.html b/views/game.html index 0bffed2..09ac599 100644 --- a/views/game.html +++ b/views/game.html @@ -10,7 +10,7 @@
-

Test was a role!

+

From fb3af5cdb9037a9a47f5c12f17ce5d2fb638e277 Mon Sep 17 00:00:00 2001 From: Maier Date: Wed, 8 Jan 2020 11:33:00 -0500 Subject: [PATCH 5/5] mobile animation styling --- static/game.js | 2 +- static/styles.css | 28 +++++++++++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/static/game.js b/static/game.js index 452f6ac..fb50f57 100644 --- a/static/game.js +++ b/static/game.js @@ -60,7 +60,7 @@ function triggerExitAnimation(e) { function triggerEntranceAnimation(selector, entranceClass, exitClass, image) { let transitionEl = document.querySelector(selector); - transitionEl.style.display = 'block'; + transitionEl.style.display = 'flex'; transitionEl.addEventListener('animationend', triggerExitAnimation, {"capture": true, "once": true}); transitionEl.classList.remove(entranceClass); transitionEl.entranceClass = entranceClass; diff --git a/static/styles.css b/static/styles.css index 32fcd4e..daae2c8 100644 --- a/static/styles.css +++ b/static/styles.css @@ -90,6 +90,19 @@ #join-game-container a button { width: 100%; } + + #overlay { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + } + + #killed-name { + padding-top: 0; + margin: 0 0.5em; + font-size: 2em; + } } @media(min-width: 750.01px) { @@ -175,6 +188,17 @@ margin-right: 1em; } + #killed-name { + padding-top: 2em; + font-size: 2.5em; + margin: 0; + } + + #overlay { + justify-content: flex-start; + align-items: center; + } + } @font-face { @@ -1036,6 +1060,7 @@ label { position: fixed; user-select: none; display: none; + flex-direction: column; width: 100%; height: 100%; top: 0; @@ -1049,9 +1074,6 @@ label { #killed-name { display: none; color: white; - padding-top: 2em; - font-size: 2.5em; - margin: 0; } #killed-role {