diff --git a/app.css b/app.css index e3b500c..da99848 100644 --- a/app.css +++ b/app.css @@ -1,6 +1,6 @@ body { background-color: #121212; - color: snow; + color: #FAF9F6; transition-duration: 3s; } @@ -31,6 +31,20 @@ ul { height: 2rem; width: 15.5rem; margin-top: 0.5rem; + background-color: #6495ED; + border: 0.05rem solid black; + box-shadow: 0 0.7rem 0 -0.1rem #6082B6, 0 0.75rem 0 -0.05rem black; + border-radius: 0.5rem; +} + +.add-item-button:hover { + transform: translate(0, 0.25rem); + box-shadow: 0 0.5rem 0 -0.1rem #6082B6, 0 0.55rem 0 -0.05rem black; +} + +.add-item-button:active { + transform: translate(0, 0.5rem); + box-shadow: 0 0.1rem 0 -0.1rem #6082B6, 0 0.15rem 0 -0.05rem black; } .close-arrow { @@ -69,6 +83,21 @@ ul { .remove-all-items-button { width: 5.4rem; + height: 2rem; + background-color: #6495ED; + border: 0.05rem solid black; + box-shadow: 0 0.7rem 0 -0.1rem #6082B6, 0 0.75rem 0 -0.05rem black; + border-radius: 0.5rem; +} + +.remove-all-items-button:hover { + transform: translate(0, 0.25rem); + box-shadow: 0 0.5rem 0 -0.1rem #6082B6, 0 0.55rem 0 -0.05rem black; +} + +.remove-all-items-button:active { + transform: translate(0, 0.5rem); + box-shadow: 0 0.1rem 0 -0.1rem #6082B6, 0 0.15rem 0 -0.05rem black; } .roulette-wheel { @@ -76,7 +105,6 @@ ul { top: 50%; left: 50%; transform: translate(-50%, -50%); - z-index: -1; margin-left: 5rem; text-align: center; } @@ -89,6 +117,7 @@ ul { .winner { font-size: 32; margin-bottom: 1rem; + height: 28px; } @media screen and (max-width: 940px) { diff --git a/src/app.ts b/src/app.ts index 335e479..93efea4 100644 --- a/src/app.ts +++ b/src/app.ts @@ -28,24 +28,32 @@ const modeEl = lightDarkModeEl.querySelector("img")! as HTMLImageElement; const canvasWrapper = document.getElementsByClassName( "roulette-wheel" )[0]! as HTMLDivElement; -const winnerEl = document.getElementsByClassName('winner')[0]! as HTMLDivElement; +const winnerEl = document.getElementsByClassName( + "winner" +)[0]! as HTMLDivElement; -const MAXIMUM_SIZE = 16; canvasEl.width = canvasWrapper.offsetWidth; canvasEl.height = canvasWrapper.offsetHeight; -const x = canvasEl.width / 2; -const y = canvasEl.height / 2; -const radius = canvasEl.height / 2 - 5; -const animationFPSRate = 30; -counterEl.textContent = `0/${MAXIMUM_SIZE}`; +const MAXIMUM_SIZE = 16; +const ROTATIONS = 20; +const ANIMATION_FPS = 60; +const WHEEL_OFFSET = 5; +const X_CENTER = canvasEl.width / 2; +const Y_CENTER = canvasEl.height / 2; +const RADIUS = canvasEl.height / 2 - WHEEL_OFFSET; const itemsList = new ItemList(MAXIMUM_SIZE); const items: MenuItem[] = []; +let rotate_by = 50; + configure(); function configure() { + counterEl.textContent = `0/${MAXIMUM_SIZE}`; + winnerEl.style.fontSize = "32px"; + LightDarkMode.currentMode = "dark"; IdPool.initializeIdsPool(MAXIMUM_SIZE); addRemoveItem(); @@ -53,46 +61,47 @@ function configure() { removeAllItems(); lightDarkMode(); drawRouletteWheel(0); + + canvasEl.addEventListener("click", startRoulette); } function addRemoveItem() { addButtonEl.addEventListener("click", () => { const name = inputEl.value; - const id = IdPool.getAnId(); - if (name && id) { - const color = avaliableRGBs[id % avaliableRGBs.length]; + if (name) { + const id = IdPool.getAnId(); + if (id) { + const color = avaliableRGBs[id % avaliableRGBs.length]; + const item = new Item(id, name, color); + const menuItem = new MenuItem(id, name, color); - const item = new Item(id, name, color); - const menuItem = new MenuItem(id, name, color); + menuItem.deleteItem.el.addEventListener("click", () => { + menuItem.el.remove(); + items.splice(items.indexOf(menuItem), 1); + itemsList.remove(item); + IdPool.addId(item.id); + updateCounter(); + drawRouletteWheel(0); + }); - menuItem.deleteItem.el.addEventListener("click", () => { - menuItem.el.remove(); - items.splice(items.indexOf(menuItem), 1); - itemsList.remove(item); - IdPool.addId(item.id); + registerChangeColorByClick(item, menuItem); + + itemsList.add(item); + items.push(menuItem); + itemListEl.appendChild(menuItem.el); updateCounter(); + guessItemIndex = Math.floor(Math.random() * items.length); + segmentAngle = 360 / items.length; + maxAngle = 360 * ROTATIONS + segmentAngle * guessItemIndex; drawRouletteWheel(0); - }); - - registerChangeColorByClick(item, menuItem); - - itemsList.add(item); - items.push(menuItem); - itemListEl.appendChild(menuItem.el); - updateCounter(); - guessItemIndex = Math.floor(Math.random() * items.length); - maxAngle = 360 * rotations + segmentAngle * guessItemIndex; - segmentAngle = 360 / items.length; - drawRouletteWheel(0); + } } }); } function addItemByEnter() { bodyEl.addEventListener("keyup", (event: KeyboardEvent) => { - if (event.key !== "Enter") { - return; - } + if (event.key !== "Enter") return; addButtonEl.click(); }); } @@ -107,7 +116,7 @@ function registerChangeColorByClick(item: Item, menuItem: MenuItem) { avaliableRGBs[(actualColorIndex + 1) % avaliableRGBs.length]; menuItem.el.style.backgroundColor = color; item.color = color; - drawRouletteWheel(0); + if (!animationId) drawRouletteWheel(0); } }); } @@ -132,13 +141,27 @@ function updateCounter() { function animateCounter() { counterEl.animate( - [{ transform: "scale(1.25)" }, { transform: "scale(1.00)" }], + [ + { transform: "scale(1.5)" }, + { transform: "scale(1.00)" }, + { transform: "" }, + ], { duration: 500, } ); } +function animateWinner() { + winnerEl.animate( + { + opacity: [0.5, 1], + easing: ["ease-in", "ease-out"], + }, + 1000 + ); +} + function lightDarkMode() { lightDarkModeEl.addEventListener("click", () => { modeEl.animate([{ transform: "rotate(360deg)" }], { @@ -147,75 +170,61 @@ function lightDarkMode() { if (LightDarkMode.currentMode == "dark") { modeEl.src = "static/sun.png"; - bodyEl.style.backgroundColor = "snow"; + bodyEl.style.backgroundColor = "#FAF9F6"; bodyEl.style.color = "black"; } else { modeEl.src = "static/moon.png"; bodyEl.style.backgroundColor = "#121212"; bodyEl.style.color = "white"; } - LightDarkMode.changeMode(); }); } -const arrowImage = document.createElement("img"); -arrowImage.src = "static/arrow-down.png"; -arrowImage.alt = "arrow"; - function drawRouletteWheel(angle: number) { const segmentWidth = 360 / items.length; let startAngle = angle; let endAngle = segmentWidth + startAngle; ctx.clearRect(0, 0, canvasEl.width, canvasEl.height); ctx.beginPath(); - // let xtemp = x + Math.cos(totalAngle/2) * radius/2; - // let ytemp = y + Math.sin(totalAngle/2) * radius/2; ctx.save(); - ctx.translate(x + radius + 50, y); + ctx.translate(X_CENTER + RADIUS + 50, Y_CENTER); ctx.lineTo(0, -20); ctx.lineTo(0, 20); ctx.lineTo(-50, 0); - ctx.fillStyle = 'white'; + ctx.fillStyle = "#C4B454"; ctx.fill(); ctx.stroke(); ctx.restore(); - ctx.arc(x, y, radius, 0, Math.PI * 2, true); + ctx.arc(X_CENTER, Y_CENTER, RADIUS, 0, Math.PI * 2, true); ctx.stroke(); for (let i = 0; i < items.length; i++) { ctx.beginPath(); - ctx.lineTo(x, y); + ctx.lineTo(X_CENTER, Y_CENTER); ctx.font = "bold 24px verdana, sans-serif"; ctx.arc( - x, - y, - radius, + X_CENTER, + Y_CENTER, + RADIUS, (startAngle * Math.PI) / 180, (endAngle * Math.PI) / 180, false ); - ctx.lineTo(x, y); + ctx.lineTo(X_CENTER, Y_CENTER); ctx.fillStyle = itemsList.items[i].color; ctx.fill(); ctx.save(); ctx.fillStyle = "black"; const text = itemsList.items[i].name; if (items.length > 1) { - const xT = - x + - (Math.cos((startAngle + segmentWidth / 2) * Math.PI / 180) * radius) / - 2; - const xY = - y + - (Math.sin((startAngle + segmentWidth / 2) * Math.PI / 180) * radius) / - 2; + const angleVal = ((startAngle + segmentWidth / 2) * Math.PI) / 180; + const xT = X_CENTER + (Math.cos(angleVal) * RADIUS) / 2; + const xY = Y_CENTER + (Math.sin(angleVal) * RADIUS) / 2; ctx.translate(xT, xY); - console.log(totalAngle); - ctx.rotate(Math.PI / items.length + Math.PI/180 * startAngle); + ctx.rotate(Math.PI / items.length + (Math.PI / 180) * startAngle); ctx.fillText(text, -ctx.measureText(text).width / 2, 0); - - }else{ - ctx.fillText(text, x, y); + } else { + ctx.fillText(text, X_CENTER, Y_CENTER); } ctx.restore(); if (items.length !== 1) { @@ -223,63 +232,62 @@ function drawRouletteWheel(angle: number) { } startAngle += segmentWidth; endAngle += segmentWidth; - // xtemp += Math.cos(totalAngle/2) * radius/2; - // ytemp += Math.sin(totalAngle/2) * radius/2; - // console.log(ytemp); } } let guessItemIndex = Math.floor(Math.random() * items.length); let segmentAngle = 360 / items.length; -const rotations = 1; -let maxAngle = 360 * rotations + segmentAngle * guessItemIndex; -let angleSpeed = 0; -let totalAngle = 0; +let maxAngle = 360 * ROTATIONS + segmentAngle * guessItemIndex; let animationId: number | null; -let startAngle = 2; -// const maxAngleSpeed = segmentAngle; +let winner: string; +let endTime: number; -function startRoulette() { - if (animationId) { - //resetRouletteAnimation(); - } else { - guessItemIndex = Math.floor(Math.random() * items.length); - console.log(guessItemIndex); - segmentAngle = 360 / items.length; - maxAngle = - 360 * rotations + - (items.length - 1 - guessItemIndex) * segmentAngle + - Math.random() * segmentAngle; - beginAnimateRoulette(); - } +function easeOut( + time: number, + beginningVal: number, + toChange: number, + duration: number +) { + return time == duration + ? beginningVal + toChange + : toChange * (-Math.pow(2, (-10 * time) / duration) + 1) + beginningVal; } +function startRoulette() { + if (!animationId) { + if (winner) { + winner = ""; + winnerEl.textContent = ""; + drawRouletteWheel(0); + } else { + guessItemIndex = Math.floor(Math.random() * items.length); + winner = itemsList.items[guessItemIndex].name; + segmentAngle = 360 / items.length; + totalTime = 0; + maxAngle = + 360 * ROTATIONS + + (items.length - 1 - guessItemIndex) * segmentAngle + + Math.random() * segmentAngle; + console.log(maxAngle); + endTime = (maxAngle / rotate_by) * (1000 / ANIMATION_FPS); + console.log(endTime); + beginAnimateRoulette(); + } + } +} +let totalTime = 0; function beginAnimateRoulette() { - if (totalAngle < maxAngle) { - drawRouletteWheel(angleSpeed); - angleSpeed += startAngle; - totalAngle += startAngle; + //TODO: animations doesn't work as expected (endTime too low or totalTime incrementing faster than expected) + if (totalTime < endTime) { + drawRouletteWheel(easeOut(totalTime, 0, maxAngle, endTime)); setTimeout(() => { animationId = window.requestAnimationFrame(beginAnimateRoulette); - }, 1000 / animationFPSRate); + totalTime += 1000 / ANIMATION_FPS; + }, 1000 / ANIMATION_FPS); } else { window.cancelAnimationFrame(animationId!); animationId = null; - angleSpeed = 1; - totalAngle = 0; - winnerEl.textContent = `Winner: ${itemsList.items[guessItemIndex].name}`; + winnerEl.textContent = `Winner: ${winner}`; + animateWinner(); } } - -// function resetRouletteAnimation(){ -// if(animationId){ -// window.cancelAnimationFrame(animationId!) -// } -// drawRouletteWheel(0); -// animationId = null; -// angleSpeed = 1; -// totalAngle = 0; -// delta = 0.2; -// } - -canvasEl.addEventListener("click", startRoulette); diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 19cdd27..04c89e1 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -1 +1,18 @@ -export const avaliableRGBs = ["rgba(255, 0, 0, 0.5)", "rgba(0, 255, 0, 0.5)", "rgba(0, 0, 255, 0.5)", "rgba(255, 255, 0, 0.5)"]; \ No newline at end of file +export const avaliableRGBs = [ + "rgba(255, 0, 0, 0.5)", + "rgba(0, 255, 0, 0.5)", + "rgba(0, 0, 255, 0.5)", + "rgba(255, 255, 0, 0.5)", + "rgba(0, 255, 255, 0.5)", + "rgba(205, 127, 50, 0.5)", + "rgba(115, 147, 179, 0.5)", + "rgba(255, 117, 24, 0.5)", + "rgba(255, 0, 255, 0.5)", + "rgba(191, 64, 191, 0.5)", + "rgba(47, 197, 114, 0.5)", + "rgba(255, 222, 173, 0.5)", + "rgba(204, 204, 255, 0.5)", + "rgba(93, 63, 211, 0.5)", + "rgba(250, 128, 114, 0.5)", + "rgba(0, 255, 127, 0.5)", +]; diff --git a/static/MFI-LIC-275177-1.pdf b/static/MFI-LIC-275177-1.pdf deleted file mode 100644 index 69292ed..0000000 Binary files a/static/MFI-LIC-275177-1.pdf and /dev/null differ diff --git a/static/arrow-down.png b/static/arrow-down.png deleted file mode 100644 index f492a74..0000000 Binary files a/static/arrow-down.png and /dev/null differ