mirror of
https://github.com/kevinveenbirkenbach/roulette-wheel.git
synced 2024-11-22 02:01:05 +01:00
button desing
This commit is contained in:
parent
861b9e34af
commit
ef26f5849b
33
app.css
33
app.css
@ -1,6 +1,6 @@
|
|||||||
body {
|
body {
|
||||||
background-color: #121212;
|
background-color: #121212;
|
||||||
color: snow;
|
color: #FAF9F6;
|
||||||
transition-duration: 3s;
|
transition-duration: 3s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,6 +31,20 @@ ul {
|
|||||||
height: 2rem;
|
height: 2rem;
|
||||||
width: 15.5rem;
|
width: 15.5rem;
|
||||||
margin-top: 0.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 {
|
.close-arrow {
|
||||||
@ -69,6 +83,21 @@ ul {
|
|||||||
|
|
||||||
.remove-all-items-button {
|
.remove-all-items-button {
|
||||||
width: 5.4rem;
|
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 {
|
.roulette-wheel {
|
||||||
@ -76,7 +105,6 @@ ul {
|
|||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
z-index: -1;
|
|
||||||
margin-left: 5rem;
|
margin-left: 5rem;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
@ -89,6 +117,7 @@ ul {
|
|||||||
.winner {
|
.winner {
|
||||||
font-size: 32;
|
font-size: 32;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
|
height: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 940px) {
|
@media screen and (max-width: 940px) {
|
||||||
|
182
src/app.ts
182
src/app.ts
@ -28,24 +28,32 @@ const modeEl = lightDarkModeEl.querySelector("img")! as HTMLImageElement;
|
|||||||
const canvasWrapper = document.getElementsByClassName(
|
const canvasWrapper = document.getElementsByClassName(
|
||||||
"roulette-wheel"
|
"roulette-wheel"
|
||||||
)[0]! as HTMLDivElement;
|
)[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.width = canvasWrapper.offsetWidth;
|
||||||
canvasEl.height = canvasWrapper.offsetHeight;
|
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 itemsList = new ItemList(MAXIMUM_SIZE);
|
||||||
const items: MenuItem[] = [];
|
const items: MenuItem[] = [];
|
||||||
|
|
||||||
|
let rotate_by = 50;
|
||||||
|
|
||||||
configure();
|
configure();
|
||||||
|
|
||||||
function configure() {
|
function configure() {
|
||||||
|
counterEl.textContent = `0/${MAXIMUM_SIZE}`;
|
||||||
|
winnerEl.style.fontSize = "32px";
|
||||||
|
|
||||||
LightDarkMode.currentMode = "dark";
|
LightDarkMode.currentMode = "dark";
|
||||||
IdPool.initializeIdsPool(MAXIMUM_SIZE);
|
IdPool.initializeIdsPool(MAXIMUM_SIZE);
|
||||||
addRemoveItem();
|
addRemoveItem();
|
||||||
@ -53,15 +61,17 @@ function configure() {
|
|||||||
removeAllItems();
|
removeAllItems();
|
||||||
lightDarkMode();
|
lightDarkMode();
|
||||||
drawRouletteWheel(0);
|
drawRouletteWheel(0);
|
||||||
|
|
||||||
|
canvasEl.addEventListener("click", startRoulette);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addRemoveItem() {
|
function addRemoveItem() {
|
||||||
addButtonEl.addEventListener("click", () => {
|
addButtonEl.addEventListener("click", () => {
|
||||||
const name = inputEl.value;
|
const name = inputEl.value;
|
||||||
|
if (name) {
|
||||||
const id = IdPool.getAnId();
|
const id = IdPool.getAnId();
|
||||||
if (name && id) {
|
if (id) {
|
||||||
const color = avaliableRGBs[id % avaliableRGBs.length];
|
const color = avaliableRGBs[id % avaliableRGBs.length];
|
||||||
|
|
||||||
const item = new Item(id, name, color);
|
const item = new Item(id, name, color);
|
||||||
const menuItem = new MenuItem(id, name, color);
|
const menuItem = new MenuItem(id, name, color);
|
||||||
|
|
||||||
@ -81,18 +91,17 @@ function addRemoveItem() {
|
|||||||
itemListEl.appendChild(menuItem.el);
|
itemListEl.appendChild(menuItem.el);
|
||||||
updateCounter();
|
updateCounter();
|
||||||
guessItemIndex = Math.floor(Math.random() * items.length);
|
guessItemIndex = Math.floor(Math.random() * items.length);
|
||||||
maxAngle = 360 * rotations + segmentAngle * guessItemIndex;
|
|
||||||
segmentAngle = 360 / items.length;
|
segmentAngle = 360 / items.length;
|
||||||
|
maxAngle = 360 * ROTATIONS + segmentAngle * guessItemIndex;
|
||||||
drawRouletteWheel(0);
|
drawRouletteWheel(0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function addItemByEnter() {
|
function addItemByEnter() {
|
||||||
bodyEl.addEventListener("keyup", (event: KeyboardEvent) => {
|
bodyEl.addEventListener("keyup", (event: KeyboardEvent) => {
|
||||||
if (event.key !== "Enter") {
|
if (event.key !== "Enter") return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
addButtonEl.click();
|
addButtonEl.click();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -107,7 +116,7 @@ function registerChangeColorByClick(item: Item, menuItem: MenuItem) {
|
|||||||
avaliableRGBs[(actualColorIndex + 1) % avaliableRGBs.length];
|
avaliableRGBs[(actualColorIndex + 1) % avaliableRGBs.length];
|
||||||
menuItem.el.style.backgroundColor = color;
|
menuItem.el.style.backgroundColor = color;
|
||||||
item.color = color;
|
item.color = color;
|
||||||
drawRouletteWheel(0);
|
if (!animationId) drawRouletteWheel(0);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -132,13 +141,27 @@ function updateCounter() {
|
|||||||
|
|
||||||
function animateCounter() {
|
function animateCounter() {
|
||||||
counterEl.animate(
|
counterEl.animate(
|
||||||
[{ transform: "scale(1.25)" }, { transform: "scale(1.00)" }],
|
[
|
||||||
|
{ transform: "scale(1.5)" },
|
||||||
|
{ transform: "scale(1.00)" },
|
||||||
|
{ transform: "" },
|
||||||
|
],
|
||||||
{
|
{
|
||||||
duration: 500,
|
duration: 500,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function animateWinner() {
|
||||||
|
winnerEl.animate(
|
||||||
|
{
|
||||||
|
opacity: [0.5, 1],
|
||||||
|
easing: ["ease-in", "ease-out"],
|
||||||
|
},
|
||||||
|
1000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function lightDarkMode() {
|
function lightDarkMode() {
|
||||||
lightDarkModeEl.addEventListener("click", () => {
|
lightDarkModeEl.addEventListener("click", () => {
|
||||||
modeEl.animate([{ transform: "rotate(360deg)" }], {
|
modeEl.animate([{ transform: "rotate(360deg)" }], {
|
||||||
@ -147,75 +170,61 @@ function lightDarkMode() {
|
|||||||
|
|
||||||
if (LightDarkMode.currentMode == "dark") {
|
if (LightDarkMode.currentMode == "dark") {
|
||||||
modeEl.src = "static/sun.png";
|
modeEl.src = "static/sun.png";
|
||||||
bodyEl.style.backgroundColor = "snow";
|
bodyEl.style.backgroundColor = "#FAF9F6";
|
||||||
bodyEl.style.color = "black";
|
bodyEl.style.color = "black";
|
||||||
} else {
|
} else {
|
||||||
modeEl.src = "static/moon.png";
|
modeEl.src = "static/moon.png";
|
||||||
bodyEl.style.backgroundColor = "#121212";
|
bodyEl.style.backgroundColor = "#121212";
|
||||||
bodyEl.style.color = "white";
|
bodyEl.style.color = "white";
|
||||||
}
|
}
|
||||||
|
|
||||||
LightDarkMode.changeMode();
|
LightDarkMode.changeMode();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const arrowImage = document.createElement("img");
|
|
||||||
arrowImage.src = "static/arrow-down.png";
|
|
||||||
arrowImage.alt = "arrow";
|
|
||||||
|
|
||||||
function drawRouletteWheel(angle: number) {
|
function drawRouletteWheel(angle: number) {
|
||||||
const segmentWidth = 360 / items.length;
|
const segmentWidth = 360 / items.length;
|
||||||
let startAngle = angle;
|
let startAngle = angle;
|
||||||
let endAngle = segmentWidth + startAngle;
|
let endAngle = segmentWidth + startAngle;
|
||||||
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
|
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
// let xtemp = x + Math.cos(totalAngle/2) * radius/2;
|
|
||||||
// let ytemp = y + Math.sin(totalAngle/2) * radius/2;
|
|
||||||
ctx.save();
|
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(0, 20);
|
ctx.lineTo(0, 20);
|
||||||
ctx.lineTo(-50, 0);
|
ctx.lineTo(-50, 0);
|
||||||
ctx.fillStyle = 'white';
|
ctx.fillStyle = "#C4B454";
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
ctx.restore();
|
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();
|
ctx.stroke();
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.lineTo(x, y);
|
ctx.lineTo(X_CENTER, Y_CENTER);
|
||||||
ctx.font = "bold 24px verdana, sans-serif";
|
ctx.font = "bold 24px verdana, sans-serif";
|
||||||
ctx.arc(
|
ctx.arc(
|
||||||
x,
|
X_CENTER,
|
||||||
y,
|
Y_CENTER,
|
||||||
radius,
|
RADIUS,
|
||||||
(startAngle * Math.PI) / 180,
|
(startAngle * Math.PI) / 180,
|
||||||
(endAngle * Math.PI) / 180,
|
(endAngle * Math.PI) / 180,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
ctx.lineTo(x, y);
|
ctx.lineTo(X_CENTER, Y_CENTER);
|
||||||
ctx.fillStyle = itemsList.items[i].color;
|
ctx.fillStyle = itemsList.items[i].color;
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.fillStyle = "black";
|
ctx.fillStyle = "black";
|
||||||
const text = itemsList.items[i].name;
|
const text = itemsList.items[i].name;
|
||||||
if (items.length > 1) {
|
if (items.length > 1) {
|
||||||
const xT =
|
const angleVal = ((startAngle + segmentWidth / 2) * Math.PI) / 180;
|
||||||
x +
|
const xT = X_CENTER + (Math.cos(angleVal) * RADIUS) / 2;
|
||||||
(Math.cos((startAngle + segmentWidth / 2) * Math.PI / 180) * radius) /
|
const xY = Y_CENTER + (Math.sin(angleVal) * RADIUS) / 2;
|
||||||
2;
|
|
||||||
const xY =
|
|
||||||
y +
|
|
||||||
(Math.sin((startAngle + segmentWidth / 2) * Math.PI / 180) * radius) /
|
|
||||||
2;
|
|
||||||
ctx.translate(xT, xY);
|
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);
|
ctx.fillText(text, -ctx.measureText(text).width / 2, 0);
|
||||||
|
} else {
|
||||||
}else{
|
ctx.fillText(text, X_CENTER, Y_CENTER);
|
||||||
ctx.fillText(text, x, y);
|
|
||||||
}
|
}
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
if (items.length !== 1) {
|
if (items.length !== 1) {
|
||||||
@ -223,63 +232,62 @@ function drawRouletteWheel(angle: number) {
|
|||||||
}
|
}
|
||||||
startAngle += segmentWidth;
|
startAngle += segmentWidth;
|
||||||
endAngle += 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 guessItemIndex = Math.floor(Math.random() * items.length);
|
||||||
let segmentAngle = 360 / items.length;
|
let segmentAngle = 360 / items.length;
|
||||||
const rotations = 1;
|
let maxAngle = 360 * ROTATIONS + segmentAngle * guessItemIndex;
|
||||||
let maxAngle = 360 * rotations + segmentAngle * guessItemIndex;
|
|
||||||
let angleSpeed = 0;
|
|
||||||
let totalAngle = 0;
|
|
||||||
let animationId: number | null;
|
let animationId: number | null;
|
||||||
let startAngle = 2;
|
let winner: string;
|
||||||
// const maxAngleSpeed = segmentAngle;
|
let endTime: number;
|
||||||
|
|
||||||
function startRoulette() {
|
function easeOut(
|
||||||
if (animationId) {
|
time: number,
|
||||||
//resetRouletteAnimation();
|
beginningVal: number,
|
||||||
} else {
|
toChange: number,
|
||||||
guessItemIndex = Math.floor(Math.random() * items.length);
|
duration: number
|
||||||
console.log(guessItemIndex);
|
) {
|
||||||
segmentAngle = 360 / items.length;
|
return time == duration
|
||||||
maxAngle =
|
? beginningVal + toChange
|
||||||
360 * rotations +
|
: toChange * (-Math.pow(2, (-10 * time) / duration) + 1) + beginningVal;
|
||||||
(items.length - 1 - guessItemIndex) * segmentAngle +
|
|
||||||
Math.random() * segmentAngle;
|
|
||||||
beginAnimateRoulette();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() {
|
function beginAnimateRoulette() {
|
||||||
if (totalAngle < maxAngle) {
|
//TODO: animations doesn't work as expected (endTime too low or totalTime incrementing faster than expected)
|
||||||
drawRouletteWheel(angleSpeed);
|
if (totalTime < endTime) {
|
||||||
angleSpeed += startAngle;
|
drawRouletteWheel(easeOut(totalTime, 0, maxAngle, endTime));
|
||||||
totalAngle += startAngle;
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
animationId = window.requestAnimationFrame(beginAnimateRoulette);
|
animationId = window.requestAnimationFrame(beginAnimateRoulette);
|
||||||
}, 1000 / animationFPSRate);
|
totalTime += 1000 / ANIMATION_FPS;
|
||||||
|
}, 1000 / ANIMATION_FPS);
|
||||||
} else {
|
} else {
|
||||||
window.cancelAnimationFrame(animationId!);
|
window.cancelAnimationFrame(animationId!);
|
||||||
animationId = null;
|
animationId = null;
|
||||||
angleSpeed = 1;
|
winnerEl.textContent = `Winner: ${winner}`;
|
||||||
totalAngle = 0;
|
animateWinner();
|
||||||
winnerEl.textContent = `Winner: ${itemsList.items[guessItemIndex].name}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// function resetRouletteAnimation(){
|
|
||||||
// if(animationId){
|
|
||||||
// window.cancelAnimationFrame(animationId!)
|
|
||||||
// }
|
|
||||||
// drawRouletteWheel(0);
|
|
||||||
// animationId = null;
|
|
||||||
// angleSpeed = 1;
|
|
||||||
// totalAngle = 0;
|
|
||||||
// delta = 0.2;
|
|
||||||
// }
|
|
||||||
|
|
||||||
canvasEl.addEventListener("click", startRoulette);
|
|
||||||
|
@ -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)"];
|
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)",
|
||||||
|
];
|
||||||
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 12 KiB |
Loading…
Reference in New Issue
Block a user