diff --git a/src/frontend/engine/GameObject.js b/src/frontend/engine/GameObject.js index dc9501f0..4697fa8d 100644 --- a/src/frontend/engine/GameObject.js +++ b/src/frontend/engine/GameObject.js @@ -81,11 +81,11 @@ const GameObject = class { this.setOrigin(); } - setOrigin(x = '50%', y = '50%') { - const numberY = makeFloat(y); - const numberX = makeFloat(x); - this.originStyle = `translate(-${x}, -${y})`; - this.instance.style.transformOrigin = `${50 - numberX}% ${50 - numberY}%`; + setOrigin(x = 50, y = 50) { + const xString = makeUnitString(x, '%'); + const yString = makeUnitString(y, '%'); + this.originStyle = `translate(-${xString}, -${yString})`; + this.instance.style.transformOrigin = `${50 - x}% ${50 - y}%`; this.transform(); } @@ -101,7 +101,7 @@ const GameObject = class { this.instance.style.zIndex = zIndex; } - move(x = 0, y = 0, duration = TIME.ONE_SECOND / 2) { + move(x = 0, y = 0, duration = TIME.DEFAULT_TRANSITION) { this.position = [x, y]; const xString = makeUnitString(x, '%'); const yString = makeUnitString(y, '%'); @@ -147,7 +147,59 @@ const GameObject = class { this.animationFrame = requestAnimationFrame(animateFunction); } - rotate(angle = 0, duration = TIME.ONE_SECOND / 5) { + roll( + x = 0, + y = 0, + duration = TIME.DEFAULT_TRANSITION, + rollCount = 1, + rollClockwise = Math.random() < 0.5 ? 1 : -1, + ) { + // TODO: move()와 겹치는 부분들 refactor... + this.position = [x, y]; + const xString = makeUnitString(x, '%'); + const yString = makeUnitString(y, '%'); + + if (this.animationFrame) cancelAnimationFrame(this.animationFrame); + if (!xString && !yString) { + this.instance.style.removeProperty('top'); + this.instance.style.removeProperty('left'); + } + if (duration === 0) { + this.instance.style.top = yString; + this.instance.style.left = xString; + return; + } + const initialY = makeFloat(this.instance.style.top) || 0; + const initialX = makeFloat(this.instance.style.left) || 0; + const targetY = y; + const targetX = x; + + let start = null; + const animateFunction = (timestamp) => { + if (!start) start = timestamp; + const elapsed = timestamp - start; + if (elapsed > duration) { + this.instance.style.top = yString; + this.instance.style.left = xString; + this.animationFrame = null; + return; + } + const newY = initialY + (targetY - initialY) * (elapsed / duration); + const newX = initialX + (targetX - initialX) * (elapsed / duration); + this.angle += rollClockwise * rollCount * 4 * (elapsed / duration); + this.rotateStyle = makeUnitString(this.angle, 'deg'); + + this.instance.style.left = makeUnitString(newX, '%'); + this.instance.style.top = makeUnitString(newY, '%'); + this.instance.style.transform = `rotateZ(${this.rotateStyle}) ${this.originStyle}`; + + requestAnimationFrame(animateFunction); + }; + + this.animationFrame = requestAnimationFrame(animateFunction); + } + + rotate(angle = 0, duration = TIME.DEFAULT_TRANSITION) { this.angle = angle; const angleString = makeUnitString(angle, 'deg'); const keyframes = [ diff --git a/src/frontend/scenes/playerWaiting/index.js b/src/frontend/scenes/playerWaiting/index.js index a757af66..acc2116b 100644 --- a/src/frontend/scenes/playerWaiting/index.js +++ b/src/frontend/scenes/playerWaiting/index.js @@ -1,8 +1,58 @@ -import rendePlayerWaiting from './render'; +import './style.scss'; +import CardObject from '@engine/CardObject'; +import DuckObject from '@engine/DuckObject'; +import { DUCK_TYPE } from '@utils/type'; +import PlayerManager from '@utils/PlayerManager'; +import { $id } from '@utils/dom'; +import renderPlayerWaiting from './render'; +import { moveMyDuck } from '../waitingRoom/events'; + +const createDuck = ({ + color = '', + x = 25 + Math.random() * 50, + y = 25 + Math.random() * 50, +} = {}) => { + const duck = new DuckObject({ type: DUCK_TYPE.CURSOR }); + duck.setColor(color); + duck.createElement(); + duck.setOriginCenter(); + duck.move(x, y, 0); + duck.setDepth(2); + duck.attachToRoot(); + return duck; +}; const PlayerWaiting = class { + constructor() { + this.cards = []; + this.ducks = new Map(); + + PlayerManager.forEach(({ socketID, ...duckData }) => { + const duck = createDuck(duckData); + this.ducks.set(socketID, duck); + }); + const myDuck = this.ducks.get(PlayerManager.currentPlayerID); + myDuck.setDepth(3); + this.duckMoveEvent = (e) => moveMyDuck(e, myDuck); + $id('root').addEventListener('click', this.duckMoveEvent); + this.dropNewCard(); + } + + dropNewCard() { + const newCard = new CardObject({ + origin: [50, 50], + position: [50, -50], + }); + newCard.setWidth(150); + newCard.angle = Math.random() * 360 - 180; + newCard.roll(40 + Math.random() * 20, 65 + Math.random() * 20, 3000); + newCard.attachToRoot(); + + this.cards = [...this.cards, newCard]; + } + render() { - const { arrayToBeRemoved = [] } = rendePlayerWaiting(); + const { arrayToBeRemoved } = renderPlayerWaiting(); this.arrayToBeRemoved = arrayToBeRemoved; } diff --git a/src/frontend/scenes/playerWaiting/playerWaiting.scss b/src/frontend/scenes/playerWaiting/playerWaiting.scss deleted file mode 100644 index e69de29b..00000000 diff --git a/src/frontend/scenes/playerWaiting/render.js b/src/frontend/scenes/playerWaiting/render.js index 5267d6d1..748168ad 100644 --- a/src/frontend/scenes/playerWaiting/render.js +++ b/src/frontend/scenes/playerWaiting/render.js @@ -1,6 +1,7 @@ -import './playerWaiting.scss'; import ProgressBarObject from '@engine/ProgressBarObject'; +import TextObject from '@engine/TextObject'; import TIME from '@utils/time'; +import TEXT from '@utils/text'; const renderPlayerWaiting = () => { const ProgressBar = new ProgressBarObject(); @@ -9,6 +10,11 @@ const renderPlayerWaiting = () => { ProgressBar.setTime(TIME.SELECT_CARD); ProgressBar.start(); + const HelpText = new TextObject(); + HelpText.addClass('player-waiting-helper-text'); + HelpText.setContent(TEXT.WAIT_PLAYER_SELECT); + HelpText.attachToRoot(); + const arrayToBeRemoved = [ProgressBar]; return { diff --git a/src/frontend/scenes/playerWaiting/style.scss b/src/frontend/scenes/playerWaiting/style.scss new file mode 100644 index 00000000..d99327d3 --- /dev/null +++ b/src/frontend/scenes/playerWaiting/style.scss @@ -0,0 +1,13 @@ +@import '@utils/common'; + +.player-waiting-helper-text { + @include helper-text-white-background; + position: absolute; + z-index: $z-index-layouts; + top: 30%; + left: 50%; + font-size: x-large; + text-align: center; + transform: translateX(-50%); + white-space: nowrap; +} diff --git a/src/frontend/utils/common.scss b/src/frontend/utils/common.scss index 868c2a58..c967d6d1 100644 --- a/src/frontend/utils/common.scss +++ b/src/frontend/utils/common.scss @@ -41,6 +41,13 @@ html { display: none; } +@mixin helper-text-white-background { + padding: 0.5em; + background-color: #fffa; + border-radius: 1em; + box-shadow: 0 0 4em 1em $white-color; +} + @mixin button { display: flex; flex-direction: row; diff --git a/src/frontend/utils/text.js b/src/frontend/utils/text.js index a03a791d..748cabcd 100644 --- a/src/frontend/utils/text.js +++ b/src/frontend/utils/text.js @@ -2,6 +2,8 @@ const TEXT = { TELLER_SELECT: '당신은 이야기꾼입니다.\n어떤 카드로 이야기를 해볼까요?', WAIT_TELLER_SELECT: '이야기꾼이 카드를 고르고 있습니다.\n잠시만 기다려주세요.', + WAIT_PLAYER_SELECT: + '다른 플레이어들이 카드를 고르고 있습니다.\n잠시만 기다려주세요.', TELLER: { true: '당신은 이야기꾼입니다.\n어떤 카드로 이야기를 해볼까요?', false: '이야기꾼이 카드를 고르고 있습니다.\n잠시만 기다려주세요.', diff --git a/src/frontend/utils/time.js b/src/frontend/utils/time.js index a81fb9d6..dfc15d7d 100644 --- a/src/frontend/utils/time.js +++ b/src/frontend/utils/time.js @@ -3,4 +3,5 @@ export default { SELECT_CARD: 60000, ONE_SECOND: 1000, HALF_SECOND: 500, + DEFAULT_TRANSITION: 200, };