mirror of
https://github.com/kevinveenbirkenbach/roulette-wheel.git
synced 2024-11-21 17:51:05 +01:00
light and darkmode change logic & disable buttons after start spin & format
This commit is contained in:
parent
3707a32c94
commit
6811b6d771
31
app.css
31
app.css
@ -1,14 +1,14 @@
|
||||
body {
|
||||
background-color: #121212;
|
||||
color: #FAF9F6;
|
||||
color: #faf9f6;
|
||||
transition-duration: 3s;
|
||||
}
|
||||
|
||||
button{
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
canvas{
|
||||
canvas {
|
||||
height: 50rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
@ -45,25 +45,24 @@ ul {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
.add-item-button {
|
||||
height: 2rem;
|
||||
width: 15.5rem;
|
||||
margin-top: 0.5rem;
|
||||
background-color: #6495ED;
|
||||
background-color: #6495ed;
|
||||
border: 0.05rem solid black;
|
||||
box-shadow: 0 0.7rem 0 -0.1rem #6082B6, 0 0.75rem 0 -0.05rem 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;
|
||||
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;
|
||||
box-shadow: 0 0.1rem 0 -0.1rem #6082b6, 0 0.15rem 0 -0.05rem black;
|
||||
}
|
||||
|
||||
.counter {
|
||||
@ -87,7 +86,7 @@ ul {
|
||||
}
|
||||
|
||||
.menu-item-list {
|
||||
text-align: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.moon-sun {
|
||||
@ -98,20 +97,20 @@ ul {
|
||||
.remove-all-items-button {
|
||||
width: 5.4rem;
|
||||
height: 2rem;
|
||||
background-color: #6495ED;
|
||||
background-color: #6495ed;
|
||||
border: 0.05rem solid black;
|
||||
box-shadow: 0 0.7rem 0 -0.1rem #6082B6, 0 0.75rem 0 -0.05rem 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;
|
||||
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;
|
||||
box-shadow: 0 0.1rem 0 -0.1rem #6082b6, 0 0.15rem 0 -0.05rem black;
|
||||
}
|
||||
|
||||
.roulette-wheel {
|
||||
@ -129,19 +128,19 @@ ul {
|
||||
}
|
||||
|
||||
.winner {
|
||||
font-size: 32;
|
||||
font-size: 32px;
|
||||
margin-bottom: 1rem;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 940px) {
|
||||
canvas{
|
||||
canvas {
|
||||
margin-top: 5rem;
|
||||
height: 20rem;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.menu{
|
||||
.menu {
|
||||
width: 23.5rem;
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="light-dark-mode">
|
||||
<img class="moon-sun" src='static/moon.png' alt="mode">
|
||||
<img class="moon-sun" src="static/moon.png" alt="mode" />
|
||||
</div>
|
||||
<div class="menu">
|
||||
<p class="counter"></p>
|
||||
@ -26,7 +26,12 @@
|
||||
</div>
|
||||
<footer>
|
||||
<div hidden="true" class="icons-authors">
|
||||
<img class="moon-sun" src='static/moon.png' alt="mode"> <img class="moon-sun" src='static/sun.png' alt="mode"> Made by made by <a href="https://www.freepik.com" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a>
|
||||
<img class="moon-sun" src="static/moon.png" alt="mode" />
|
||||
<img class="moon-sun" src="static/sun.png" alt="mode" /> Made by made by
|
||||
<a href="https://www.freepik.com" title="Freepik">Freepik</a> from
|
||||
<a href="https://www.flaticon.com/" title="Flaticon"
|
||||
>www.flaticon.com</a
|
||||
>
|
||||
</div>
|
||||
<p>Icons</p>
|
||||
</footer>
|
||||
|
202
src/app.ts
202
src/app.ts
@ -22,15 +22,20 @@ const lightDarkModeEl = document.getElementsByClassName(
|
||||
"light-dark-mode"
|
||||
)[0]! as HTMLDivElement;
|
||||
const canvasEl = document.querySelector("canvas")! as HTMLCanvasElement;
|
||||
const ctx = canvasEl.getContext("2d")!;
|
||||
const bodyEl = document.querySelector("body")! as HTMLBodyElement;
|
||||
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 footerEl = document.querySelector("footer")!;
|
||||
const authorsEl = document.getElementsByClassName(
|
||||
"icons-authors"
|
||||
)[0]! as HTMLDivElement;
|
||||
const canvasWrapper = document.getElementsByClassName(
|
||||
"roulette-wheel"
|
||||
)[0]! as HTMLDivElement;
|
||||
|
||||
const ctx = canvasEl.getContext("2d")!;
|
||||
|
||||
canvasEl.width = canvasWrapper.offsetWidth;
|
||||
canvasEl.height = canvasWrapper.offsetHeight;
|
||||
@ -47,12 +52,12 @@ const itemsList = new ItemList(MAXIMUM_SIZE);
|
||||
const items: MenuItem[] = [];
|
||||
|
||||
let rotate_by = 50;
|
||||
let totalTime = 0;
|
||||
|
||||
configure();
|
||||
|
||||
function configure() {
|
||||
counterEl.textContent = `0/${MAXIMUM_SIZE}`;
|
||||
winnerEl.style.fontSize = "32px";
|
||||
|
||||
LightDarkMode.currentMode = "dark";
|
||||
IdPool.initializeIdsPool(MAXIMUM_SIZE);
|
||||
@ -63,37 +68,47 @@ function configure() {
|
||||
drawRouletteWheel(0);
|
||||
|
||||
canvasEl.addEventListener("click", startRoulette);
|
||||
footerEl.addEventListener("click", () => {
|
||||
authorsEl.hidden = !authorsEl.hidden;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function addRemoveItem() {
|
||||
addButtonEl.addEventListener("click", () => {
|
||||
const name = inputEl.value;
|
||||
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);
|
||||
if (!animationId) {
|
||||
clearWinner();
|
||||
const name = inputEl.value;
|
||||
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);
|
||||
|
||||
menuItem.deleteItem.el.addEventListener("click", () => {
|
||||
menuItem.el.remove();
|
||||
items.splice(items.indexOf(menuItem), 1);
|
||||
itemsList.remove(item);
|
||||
IdPool.addId(item.id);
|
||||
menuItem.deleteItem.el.addEventListener("click", () => {
|
||||
if (!animationId) {
|
||||
clearWinner();
|
||||
menuItem.el.remove();
|
||||
items.splice(items.indexOf(menuItem), 1);
|
||||
itemsList.remove(item);
|
||||
IdPool.addId(item.id);
|
||||
updateCounter();
|
||||
drawRouletteWheel(0);
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
segmentAngle = 360 / items.length;
|
||||
maxAngle = 360 * ROTATIONS + segmentAngle * guessItemIndex;
|
||||
drawRouletteWheel(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -108,32 +123,59 @@ function addItemByEnter() {
|
||||
|
||||
function registerChangeColorByClick(item: Item, menuItem: MenuItem) {
|
||||
menuItem.el.addEventListener("click", () => {
|
||||
const actualColorIndex = avaliableRGBs.indexOf(
|
||||
menuItem.el.style.backgroundColor
|
||||
);
|
||||
if (actualColorIndex !== -1) {
|
||||
const color =
|
||||
avaliableRGBs[(actualColorIndex + 1) % avaliableRGBs.length];
|
||||
menuItem.el.style.backgroundColor = color;
|
||||
item.color = color;
|
||||
if (!animationId) drawRouletteWheel(0);
|
||||
if (!animationId) {
|
||||
const actualColorIndex = avaliableRGBs.indexOf(
|
||||
menuItem.el.style.backgroundColor
|
||||
);
|
||||
if (actualColorIndex !== -1) {
|
||||
const color =
|
||||
avaliableRGBs[(actualColorIndex + 1) % avaliableRGBs.length];
|
||||
menuItem.el.style.backgroundColor = color;
|
||||
item.color = color;
|
||||
drawRouletteWheel(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeAllItems() {
|
||||
removeAllItemsButtonEl.addEventListener("click", () => {
|
||||
if (items.length > 0) {
|
||||
items.forEach((menuItem) => menuItem.el.remove());
|
||||
items.length = 0;
|
||||
itemsList.clear();
|
||||
IdPool.initializeIdsPool(MAXIMUM_SIZE);
|
||||
updateCounter();
|
||||
drawRouletteWheel(0);
|
||||
if (!animationId) {
|
||||
clearWinner();
|
||||
if (items.length > 0) {
|
||||
items.forEach((menuItem) => menuItem.el.remove());
|
||||
items.length = 0;
|
||||
itemsList.clear();
|
||||
IdPool.initializeIdsPool(MAXIMUM_SIZE);
|
||||
updateCounter();
|
||||
drawRouletteWheel(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function lightDarkMode() {
|
||||
lightDarkModeEl.addEventListener("click", () => {
|
||||
modeEl.animate([{ transform: "rotate(360deg)" }], {
|
||||
duration: 500,
|
||||
});
|
||||
|
||||
if (LightDarkMode.currentMode == "dark") {
|
||||
modeEl.src = "static/sun.png";
|
||||
bodyEl.style.backgroundColor = "#FAF9F6";
|
||||
bodyEl.style.color = "black";
|
||||
LightDarkMode.currentMode = "light";
|
||||
} else {
|
||||
modeEl.src = "static/moon.png";
|
||||
bodyEl.style.backgroundColor = "#121212";
|
||||
bodyEl.style.color = "white";
|
||||
LightDarkMode.currentMode = "dark";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function updateCounter() {
|
||||
counterEl.textContent = `${itemsList.items.length}/${MAXIMUM_SIZE}`;
|
||||
animateCounter();
|
||||
@ -141,11 +183,7 @@ function updateCounter() {
|
||||
|
||||
function animateCounter() {
|
||||
counterEl.animate(
|
||||
[
|
||||
{ transform: "scale(1.5)" },
|
||||
{ transform: "scale(1.00)" },
|
||||
{ transform: "" },
|
||||
],
|
||||
[{ transform: "scale(1.5)" }, { transform: "scale(1.00)" }],
|
||||
{
|
||||
duration: 500,
|
||||
}
|
||||
@ -162,25 +200,6 @@ function animateWinner() {
|
||||
);
|
||||
}
|
||||
|
||||
function lightDarkMode() {
|
||||
lightDarkModeEl.addEventListener("click", () => {
|
||||
modeEl.animate([{ transform: "rotate(360deg)" }], {
|
||||
duration: 500,
|
||||
});
|
||||
|
||||
if (LightDarkMode.currentMode == "dark") {
|
||||
modeEl.src = "static/sun.png";
|
||||
bodyEl.style.backgroundColor = "#FAF9F6";
|
||||
bodyEl.style.color = "black";
|
||||
} else {
|
||||
modeEl.src = "static/moon.png";
|
||||
bodyEl.style.backgroundColor = "#121212";
|
||||
bodyEl.style.color = "white";
|
||||
}
|
||||
LightDarkMode.changeMode();
|
||||
});
|
||||
}
|
||||
|
||||
function drawRouletteWheel(angle: number) {
|
||||
const segmentWidth = 360 / items.length;
|
||||
let startAngle = angle;
|
||||
@ -242,24 +261,14 @@ let animationId: number | null;
|
||||
let winner: string;
|
||||
let endTime: number;
|
||||
|
||||
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 = "";
|
||||
const winnerCleared = clearWinner();
|
||||
if (winnerCleared) {
|
||||
drawRouletteWheel(0);
|
||||
} else {
|
||||
addButtonEl.style.background = "#C0C0C0";
|
||||
removeAllItemsButtonEl.style.background = "#C0C0C0";
|
||||
guessItemIndex = Math.floor(Math.random() * items.length);
|
||||
winner = itemsList.items[guessItemIndex].name;
|
||||
segmentAngle = 360 / items.length;
|
||||
@ -274,8 +283,15 @@ function startRoulette() {
|
||||
}
|
||||
}
|
||||
|
||||
function clearWinner() {
|
||||
if (winner) {
|
||||
winner = "";
|
||||
winnerEl.textContent = "";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
let totalTime = 0;
|
||||
function beginAnimateRoulette() {
|
||||
const angle = easeOut(totalTime, 0, maxAngle, endTime);
|
||||
if (totalTime < endTime || maxAngle - angle > 0.1) {
|
||||
@ -288,12 +304,20 @@ function beginAnimateRoulette() {
|
||||
window.cancelAnimationFrame(animationId!);
|
||||
animationId = null;
|
||||
winnerEl.textContent = `Winner: ${winner}`;
|
||||
addButtonEl.style.background = "#6082B6";
|
||||
removeAllItemsButtonEl.style.background = "#6082B6";
|
||||
animateWinner();
|
||||
}
|
||||
}
|
||||
|
||||
const footerEl = document.querySelector('footer')!;
|
||||
const authorsEl = document.getElementsByClassName('icons-authors')[0]! as HTMLDivElement;
|
||||
footerEl.addEventListener('click', () => {
|
||||
authorsEl.hidden = !authorsEl.hidden;
|
||||
});
|
||||
|
||||
function easeOut(
|
||||
time: number,
|
||||
beginningVal: number,
|
||||
toChange: number,
|
||||
duration: number
|
||||
) {
|
||||
return time == duration
|
||||
? beginningVal + toChange
|
||||
: toChange * (-Math.pow(2, (-10 * time) / duration) + 1) + beginningVal;
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
export abstract class Component<T extends HTMLElement> {
|
||||
el: T;
|
||||
el: T;
|
||||
|
||||
constructor(el: T){
|
||||
this.el = el;
|
||||
}
|
||||
constructor(el: T) {
|
||||
this.el = el;
|
||||
}
|
||||
|
||||
removeChildren() {
|
||||
if(this.el.childNodes.length > 0){
|
||||
while(this.el.firstChild){
|
||||
this.el.removeChild(this.el.firstChild);
|
||||
}
|
||||
}
|
||||
removeChildren() {
|
||||
if (this.el.childNodes.length > 0) {
|
||||
while (this.el.firstChild) {
|
||||
this.el.removeChild(this.el.firstChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,13 +1,12 @@
|
||||
import { Component } from "./base-component";
|
||||
|
||||
export class ItemRemoval extends Component<HTMLImageElement>{
|
||||
|
||||
constructor(id: number){
|
||||
const el = document.createElement('img');
|
||||
el.src = 'static/close.png';
|
||||
el.alt = 'delete';
|
||||
el.className = `item-delete`;
|
||||
el.id = `${id}-delete`
|
||||
super(el);
|
||||
}
|
||||
export class ItemRemoval extends Component<HTMLImageElement> {
|
||||
constructor(id: number) {
|
||||
const el = document.createElement("img");
|
||||
el.src = "static/close.png";
|
||||
el.alt = "delete";
|
||||
el.className = `item-delete`;
|
||||
el.id = `${id}-delete`;
|
||||
super(el);
|
||||
}
|
||||
}
|
@ -2,28 +2,27 @@ import { Component } from "./base-component";
|
||||
import { ItemRemoval } from "./item-removal";
|
||||
|
||||
export class MenuItem extends Component<HTMLLIElement> {
|
||||
deleteItem: ItemRemoval;
|
||||
|
||||
deleteItem: ItemRemoval;
|
||||
constructor(id: number, name: string, color: string) {
|
||||
const deleteItem = new ItemRemoval(id);
|
||||
const el = document.createElement("li");
|
||||
el.className = "menu-item";
|
||||
el.id = `${id}-item`;
|
||||
el.textContent = name;
|
||||
el.style.backgroundColor = color;
|
||||
el.appendChild(deleteItem.el);
|
||||
super(el);
|
||||
this.deleteItem = deleteItem;
|
||||
this.appearAnimation();
|
||||
}
|
||||
|
||||
constructor(id: number, name: string, color: string){
|
||||
const deleteItem = new ItemRemoval(id);
|
||||
const el = document.createElement('li');
|
||||
el.className = 'menu-item';
|
||||
el.id = `${id}-item`;
|
||||
el.textContent = name;
|
||||
el.style.backgroundColor = color;
|
||||
el.appendChild(deleteItem.el);
|
||||
super(el);
|
||||
this.deleteItem = deleteItem;
|
||||
this.appearAnimation();
|
||||
}
|
||||
|
||||
appearAnimation(){
|
||||
this.el.animate(
|
||||
[{ transform: "scale(1.25)" }, { transform: "scale(1.00)" }],
|
||||
{
|
||||
duration: 500,
|
||||
}
|
||||
);
|
||||
}
|
||||
appearAnimation() {
|
||||
this.el.animate(
|
||||
[{ transform: "scale(1.25)" }, { transform: "scale(1.00)" }],
|
||||
{
|
||||
duration: 500,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
@ -2,31 +2,30 @@ import { Item } from "./item";
|
||||
import { List } from "./list";
|
||||
|
||||
export class ItemList implements List<Item> {
|
||||
items: Item[];
|
||||
maximumSize: number;
|
||||
items: Item[];
|
||||
maximumSize: number;
|
||||
|
||||
constructor(maxSize: number){
|
||||
this.items = [];
|
||||
this.maximumSize = maxSize;
|
||||
}
|
||||
constructor(maxSize: number) {
|
||||
this.items = [];
|
||||
this.maximumSize = maxSize;
|
||||
}
|
||||
|
||||
add(item: Item){
|
||||
if(this.items.length < this.maximumSize)
|
||||
this.items.push(item);
|
||||
}
|
||||
add(item: Item) {
|
||||
if (this.items.length < this.maximumSize) this.items.push(item);
|
||||
}
|
||||
|
||||
removeById(id: number){
|
||||
const toRemove = this.items.find(e => e.id === id);
|
||||
if(toRemove){
|
||||
this.remove(toRemove);
|
||||
}
|
||||
removeById(id: number) {
|
||||
const toRemove = this.items.find((e) => e.id === id);
|
||||
if (toRemove) {
|
||||
this.remove(toRemove);
|
||||
}
|
||||
}
|
||||
|
||||
remove(item: Item){
|
||||
this.items.splice(this.items.indexOf(item), 1);
|
||||
}
|
||||
remove(item: Item) {
|
||||
this.items.splice(this.items.indexOf(item), 1);
|
||||
}
|
||||
|
||||
clear(){
|
||||
this.items = [];
|
||||
}
|
||||
clear() {
|
||||
this.items = [];
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
export interface List<T> {
|
||||
items: T[];
|
||||
items: T[];
|
||||
|
||||
add(item: T): void;
|
||||
add(item: T): void;
|
||||
}
|
@ -16,7 +16,7 @@ export abstract class IdPool {
|
||||
return id;
|
||||
}
|
||||
|
||||
static addId(id: number){
|
||||
static addId(id: number) {
|
||||
this.avaliableIds.push(id);
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,5 @@
|
||||
type mode = 'dark' | 'light';
|
||||
type mode = "dark" | "light";
|
||||
|
||||
export abstract class LightDarkMode{
|
||||
static currentMode: mode;
|
||||
|
||||
static changeMode(){
|
||||
if(this.currentMode == 'light'){
|
||||
this.currentMode = 'dark';
|
||||
}else{
|
||||
this.currentMode = 'light';
|
||||
}
|
||||
}
|
||||
export abstract class LightDarkMode {
|
||||
static currentMode: mode;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user