Merge pull request #12 from Aleksey-Levin/dev_frontend

Dev frontend
This commit is contained in:
LewinUp 2024-01-09 16:21:19 +08:00 committed by GitHub
commit 3063dd44bd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 381 additions and 23 deletions

View file

@ -37,4 +37,4 @@ export const AppNav = observer(() => {
</div> </div>
</div> </div>
) )
}) })

View file

@ -0,0 +1,103 @@
import { useState, useEffect, useRef, useLayoutEffect } from "react";
import PropTypes from "prop-types";
import symbols2 from "../../../pages/SlotGamePage/symbols2";
function Reel({ isHorizontal, rng, rngReverse, cellCount }) {
const carousel = useRef(null);
const refs = useRef([]);
const cells = refs.current.map((value) => value);
const [selectedIndex, setSelectedIndex] = useState(null);
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
let rotateFn = isHorizontal ? "rotateY" : "rotateX";
let radius;
let theta;
let cellAngle;
function randomNumberInRange(min, max) {
// 👇️ get number between min (inclusive) and max (inclusive)
return Math.floor(Math.random() * (max - min + 1)) + min;
}
useEffect(() => {
return cellCount < 7
? setSelectedIndex(selectedIndex + randomNumberInRange(3, 6))
: setSelectedIndex(selectedIndex + randomNumberInRange(6, 14));
}, [rng]);
useEffect(() => {
return cellCount < 7
? setSelectedIndex(selectedIndex + randomNumberInRange(-3, -6))
: setSelectedIndex(selectedIndex + randomNumberInRange(-6, -14));
}, [rngReverse]);
useLayoutEffect(() => {
setWidth(carousel.current.offsetWidth);
setHeight(carousel.current.offsetHeight);
setSelectedIndex(0);
}, []);
useEffect(() => {
theta = 360 / cellCount;
rotateFn = isHorizontal ? "rotateY" : "rotateX";
const angle = theta * selectedIndex * -1;
carousel.current.style.transform = `translateZ(${-radius}px) ${rotateFn}(${angle}deg)`;
}, [selectedIndex]);
function rotateCarousel() {
const angles = theta * selectedIndex * -1;
carousel.current.style.transform = `translateZ(${-radius}px) ${rotateFn}(${angles}deg)`;
}
function changeCarousel() {
theta = 360 / cellCount;
const cellSize = isHorizontal ? width : height;
radius = Math.round(cellSize / 2 / Math.tan(Math.PI / cellCount));
// eslint-disable-next-line no-plusplus
for (let i = 0; i < cells.length; i++) {
const cell = cells[i];
if (i < cellCount) {
cell.style.opacity = 1;
cellAngle = theta * i;
cell.style.transform = `${rotateFn}(${cellAngle}deg) translateZ(${radius}px)`;
rotateCarousel();
} else {
cell.style.opacity = 0;
cell.style.transform = "none";
}
}
}
function onOrientationChange() {
rotateFn = isHorizontal ? "rotateY" : "rotateX";
changeCarousel();
}
// set initials
onOrientationChange();
return (
<div className="scene">
<div className="carousel" ref={carousel}>
{symbols2.map((c, index) => (
<div
key={c.id}
className={`carousel__cell flex bg-white ${c.transform}`}
ref={(element) => {
refs.current[index] = element;
}}
>
<img src={c.src} alt="..." className="h-24 w-24 m-auto" />
</div>
))}
</div>
</div>
);
}
Reel.propTypes = {
isHorizontal: PropTypes.bool.isRequired,
cellCount: PropTypes.oneOfType([PropTypes.string, PropTypes.number])
.isRequired,
rng: PropTypes.bool.isRequired,
rngReverse: PropTypes.bool.isRequired,
};
export default Reel;

View file

@ -0,0 +1,7 @@
export const RPSPlayButton = () => {
return (
<button className="bg-rose-700 rounded-[100px] shadow text-white text-2xl font-bold py-5 px-10 hover:bg-rose-600 inline-block max-w-[200px]">
Играть
</button >
)
}

View file

@ -0,0 +1,7 @@
export const SlotPlayButton = () => {
return (
<button className="bg-rose-700 rounded-[100px] shadow text-white text-2xl font-bold py-5 px-10 hover:bg-rose-600">
Крутить
</button>
)
}

View file

@ -18,15 +18,7 @@ export const MainPage = () => {
<div className="absolute inset-0 bg-gradient-to-r from-[#171A21] via-[rgba(22, 25, 32, 0.10)] to-[#171A21] via-[rgba(22,22,22,0)]" /> <div className="absolute inset-0 bg-gradient-to-r from-[#171A21] via-[rgba(22, 25, 32, 0.10)] to-[#171A21] via-[rgba(22,22,22,0)]" />
<div className="absolute inset-0 bg-gradient-to-t from-[#171A21] via-[rgba(22, 25, 32, 0.10)] to-[#171A21] via-[rgba(22,22,22,0)]" /> <div className="absolute inset-0 bg-gradient-to-t from-[#171A21] via-[rgba(22, 25, 32, 0.10)] to-[#171A21] via-[rgba(22,22,22,0)]" />
</div> </div>
<div className="text-white text-[46px] font-bold mb-[300px] relative z-10 mt-[100px]"> <div className="w-full h-max-content bg-gray-800 rounded-[25px] p-[30px] text-white flex flex-col gap-[30px] relative z-10 mt-[400px]">
<span>
GET UP TO </span>
<span className="text-amber-400">$1500</span>
<span> BONUS
</span>
<div className="text-gray-400 text-[25px] font-bold">REGISTER AND GET YOUR BONUS</div>
</div>
<div className="w-full h-max-content bg-gray-800 rounded-[25px] p-[30px] text-white flex flex-col gap-[30px] relative z-10">
<div className="flex flex-row justify-between"> <div className="flex flex-row justify-between">
<div className="flex flex-row items-center gap-2"> <div className="flex flex-row items-center gap-2">
<GamepadIcon /> <GamepadIcon />
@ -46,7 +38,7 @@ export const MainPage = () => {
</div> </div>
</div> </div>
</div> </div>
<div className="flex flex-row justify-evenly"> <div className="flex flex-row justify-between">
{/* dices */} {/* dices */}
<div className="w-[374px] h-[187.35px] bg-[#323846] rounded-[23px] border-2 border-gray-700 flex flex-row items-center justify-between px-4 py-6 gap-3"> <div className="w-[374px] h-[187.35px] bg-[#323846] rounded-[23px] border-2 border-gray-700 flex flex-row items-center justify-between px-4 py-6 gap-3">
<div className="rounded-full bg-neutral-800 inline-block"> <div className="rounded-full bg-neutral-800 inline-block">
@ -57,7 +49,7 @@ export const MainPage = () => {
/> />
</div> </div>
<div className="flex flex-col h-full justify-between"> <div className="flex flex-col h-full justify-between">
<div className="flex flex-row justify-between min-w-[200px]"> <div className="flex flex-row justify-between min-w-[200px] my-auto items-center">
<span className="text-xl font-bold">DICE</span> <span className="text-xl font-bold">DICE</span>
<Link to="dice" className="min-w-[100px] max-h-[45px] bg-blue-500 rounded-[10px] border border-blue-400 font-bold px-3 items-center flex justify-center">Play now</Link> <Link to="dice" className="min-w-[100px] max-h-[45px] bg-blue-500 rounded-[10px] border border-blue-400 font-bold px-3 items-center flex justify-center">Play now</Link>
</div> </div>
@ -73,14 +65,13 @@ export const MainPage = () => {
/> />
</div> </div>
<div className="flex flex-col h-full justify-between"> <div className="flex flex-col h-full justify-between">
<div className="flex flex-row justify-between min-w-[200px]"> <div className="flex flex-row justify-between min-w-[200px] my-auto">
<div className="flex flex-col"> <span className="text-xl font-bold uppercase">Rock paper scissors</span>
<span className="text-xl font-bold uppercase">Rock paper scissors</span> <a href="/rock-paper-scissors" className="min-w-[100px]max-h-[45px] bg-blue-500 rounded-[10px] border border-blue-400 font-bold px-3 items-center flex justify-center py-2">Play now</a>
</div>
<button className="bg-blue-500 min-w-[103px] max-h-[45px] rounded-[10px] border border-blue-400 font-bold px-3 inline-block">Play now</button>
</div> </div>
</div> </div>
</div> </div>
{/* slot */}
<div className="w-[374px] h-[187.35px] bg-[#323846] rounded-[23px] border-2 border-gray-700 flex flex-row items-center justify-between px-4 py-6 gap-3"> <div className="w-[374px] h-[187.35px] bg-[#323846] rounded-[23px] border-2 border-gray-700 flex flex-row items-center justify-between px-4 py-6 gap-3">
<div className="rounded-full bg-neutral-800 inline-block"> <div className="rounded-full bg-neutral-800 inline-block">
<img <img
@ -90,11 +81,9 @@ export const MainPage = () => {
/> />
</div> </div>
<div className="flex flex-col h-full justify-between"> <div className="flex flex-col h-full justify-between">
<div className="flex flex-row justify-between min-w-[200px]"> <div className="flex flex-row justify-between min-w-[200px] my-auto">
<div className="flex flex-col"> <span className="text-xl font-bold uppercase">Slot machine</span>
<span className="text-xl font-bold uppercase">Slot machine</span> <a href="/slot" className="min-w-[100px]max-h-[45px] bg-blue-500 rounded-[10px] border border-blue-400 font-bold px-3 items-center flex justify-center py-2">Play now</a>
</div>
<button className="bg-blue-500 min-w-[103px] max-h-[45px] rounded-[10px] border border-blue-400 font-bold px-3 inline-block">Play now</button>
</div> </div>
</div> </div>
</div> </div>

View file

@ -0,0 +1,57 @@
import React, { useState } from 'react';
import ContainerLayout from "../../utils/ContainerLayout"
import { RPSPlayButton } from "../../components/web3/RPSPlayButton"
export const RockPaperScissorsGamePage = () => {
const [userChoice, setUserChoice] = useState('src/assets/img/RPS-rock.png');
const handleImageClick = () => {
// cycle
if (userChoice === 'src/assets/img/RPS-rock.png') {
setUserChoice('src/assets/img/RPS-paper.png');
} else if (userChoice === 'src/assets/img/RPS-paper.png') {
setUserChoice('src/assets/img/RPS-scissors.png');
} else {
setUserChoice('src/assets/img/RPS-rock.png');
}
};
return (
<ContainerLayout>
<div className="grid grid-cols-2">
<div className="rounded">
<p className="text-[46px] font-bold uppercase bg-slate-400 rounded inline-block p-4 m-2">
ты
</p>
</div>
<div className="rounded">
<p className="text-[46px] font-bold uppercase bg-slate-400 rounded inline-block p-4 m-2">
не ты
</p>
</div>
</div>
<div className="grid grid-cols-2">
<div className="w-[500px] h-[500px]">
<img src={userChoice} alt="your_hand" onClick={handleImageClick} />
</div>
<div className="w-[500px] h-[500px]">
<img src="src/assets/img/RPS-rock.png" alt="casino_hand" />
</div>
</div>
<div className="grid grid-cols-3 items-center">
<img src="src/assets/img/RPS-rules.png" alt="rules" />
<div className="max-h-[100px] flex items-center justify-center">
<RPSPlayButton />
</div>
<div className="bg-green-700 shadow text-white text-2xl font-bold py-4 px-6">
<p>won: 5 times</p>
<p>lost: 2 times</p>
</div>
</div>
</ContainerLayout>
)
}

View file

@ -0,0 +1,67 @@
import ContainerLayout from "../../utils/ContainerLayout"
import PropTypes from 'prop-types';
import Reel from '../../components/App/Slot/Reel';
import { useEffect, useState } from 'react';
import { RPSPlayButton } from '../../components/web3/RPSPlayButton';
export const SlotGamePage = () => {
const [isHorizontal, setIsHorizontal] = useState(false)
const [cellCount, setCellCount] = useState(7)
const [rng, setRng] = useState(false)
const [rngReverse, setRngReverse] = useState(false)
const mql = window.matchMedia('(orientation: portrait)')
mql.onchange = (e) => {
if (e.matches) {
setIsHorizontal(true)
} else {
setIsHorizontal(false)
}
}
const handleRng = () => {
setRng(!rng)
}
const handleRngReverse = () => {
setRngReverse(!rngReverse)
}
useEffect(() => {
if (mql.matches) {
setIsHorizontal(true)
} else {
setIsHorizontal(false)
}
}, [])
return (
<ContainerLayout>
<div className="flex flex-row gap-4">
<div className="max-h-[180px] bg-green-700 flex flex-col justify-between shadow text-white text-2xl font-bold py-4 px-6 rounded-xl">
<div className="flex flex-col">
<p>выигрыш:</p><span className="text-yellow-400">5000</span>
</div>
<div className="flex flex-col">
<p>ставка:</p><span className="text-yellow-400">10 000</span>
</div>
</div>
<div className="min-h-[800px] w-full bg-fuchsia-600 flex items-center justify-center">
<div className="flex flex-row portrait:flex-col justify-center items-center my-auto">
<Reel rng={rng} rngReverse={rngReverse} cellCount={cellCount} isHorizontal={isHorizontal} />
<Reel rng={rng} rngReverse={rngReverse} cellCount={cellCount} isHorizontal={isHorizontal} />
<Reel rng={rng} rngReverse={rngReverse} cellCount={cellCount} isHorizontal={isHorizontal} />
</div>
</div>
<div className="max-w-[100px]">
<RPSPlayButton />
</div>
</div>
</ContainerLayout>
);
}
SlotGamePage.propTypes = {
cellCount: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired
};

View file

@ -0,0 +1,82 @@
import bar from '../../../assets/img/slot/bar-512.png'
import bell from '../../../assets/img/slot/bell-512.png'
import cherries from '../../../assets/img/slot/cherries-512.png'
import crown from '../../../assets/img/slot/crown-512.png'
import diamond from '../../../assets/img/slot/diamond-512.png'
import horseshoe from '../../../assets/img/slot/horseshoe-512.png'
import seven from '../../../assets/img/slot/seven-512.png'
import watermelon from '../../../assets/img/slot/watermelon-512.png'
const symbols2 = [
{
transform: 'rotateY(0deg) translateZ(288px)',
src: seven,
id: 1,
},
{
transform: 'rotateY(40deg) translateZ(288px)',
src: bar,
id: 2,
},
{
transform: 'rotateY(80deg) translateZ(288px)',
src: bell,
id: 3,
},
{
transform: 'rotateY(120deg) translateZ(288px)',
src: cherries,
id: 4,
},
{
transform: 'rotateY(160deg) translateZ(288px)',
src: horseshoe,
id: 5,
},
{
transform: 'rotateY(200deg) translateZ(288px)',
src: bar,
id: 6,
},
{
transform: 'rotateY(240deg) translateZ(288px)',
src: crown,
id: 7,
},
{
transform: 'rotateY(280deg) translateZ(288px)',
src: diamond,
id: 8,
},
{
transform: 'rotateY(320deg) translateZ(288px)',
src: bar,
id: 9,
},
{
src: horseshoe,
id: 10,
},
{
src: bar,
id: 11,
},
{
src: seven,
id: 12,
},
{
src: bar,
id: 13,
},
{
src: watermelon,
id: 14,
},
{
src: cherries,
id: 15,
},
]
export default symbols2

View file

@ -4,6 +4,8 @@ import { AppLayout } from '../components/App'
import { MainPage } from "./MainPage/MainPage.tsx"; import { MainPage } from "./MainPage/MainPage.tsx";
import { NotMainPage } from "./NotMainPage/NotMainPage.tsx"; import { NotMainPage } from "./NotMainPage/NotMainPage.tsx";
import { DiceGamePage } from './DiceGamePage/DiceGamePage.tsx'; import { DiceGamePage } from './DiceGamePage/DiceGamePage.tsx';
import { RockPaperScissorsGamePage } from './RockPaperScissorsGamePage/RockPaperScissorsGamePage.tsx';
import { SlotGamePage } from './SlotGamePage/SlotGamePage.tsx';
const routes: RouteObject[] = [ const routes: RouteObject[] = [
{ {
@ -18,6 +20,14 @@ const routes: RouteObject[] = [
path: '/dice', path: '/dice',
element: <DiceGamePage />, element: <DiceGamePage />,
}, },
{
path: '/rock-paper-scissors',
element: <RockPaperScissorsGamePage />,
},
{
path: '/slot',
element: <SlotGamePage />,
},
{ {
path: '*', path: '*',
element: <Navigate replace to={'/'} />, element: <Navigate replace to={'/'} />,

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1,3 +1,39 @@
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
.scene {
border: 1px solid #ccc;
position: relative;
width: 210px;
height: 140px;
perspective: 1500px;
}
.carousel {
width: 100%;
height: 100%;
position: absolute;
transform: translateZ(-288px);
transform-style: preserve-3d;
transition: transform 1.4s;
transition-timing-function: ease-in-out;
}
.carousel__cell {
position: absolute;
width: 190px;
height: 120px;
left: 10px;
top: 10px;
border: 2px solid black;
line-height: 116px;
font-size: 80px;
font-weight: bold;
color: white;
text-align: center;
transition:
transform 1s,
opacity 1s;
backface-visibility: hidden;
}