Game Night is to have fun alone or with friends, even through a pandemic.
Play a hangman game with movie titles in your favorite Language! (choosen from popular movies on OMDBApi & see the poster passed from themoviedb.org) For each wrong guess a associated giphy (fetched from Giphy API) is revealed - failure is not bad đź‘ľ
I used React Context to pass the logic of the game. I did some animations utilizing SCSS - will you find an easteregg? With Formspree the Input of the Contactform is sent to my Email.
Demo
·
Report Bug
- React
- React Context
- React Router
- React Transition Groups
- SCSS
- Axios
- Formspree
- Open Movie API
- The Movie DB API
- Developer Joke API
- Giphy API
I started with the idea: a gameing site to have fun alone or with friends in an easy way during lockdown of Covid19. I choosed to do Hangman, which is commenly known and easy to learn even if you don't know it yet. With games you can create an atmosephere of a specifiy world - i utilized movies for the athmosphere in hangman, retro arcade athetics for the home page and retro monitor athetics for the contact page.
Look at a sign at the wall and if you click on the font, reach the entrance
- scale pic to show the wall,
- onclick: scale pic to cover whole Screen
.entranceBackground {
background-image: url(arcadeEntrance.jpg);
background-color: black;
background-size: cover;
background-repeat: no-repeat;
animation-name: divMove;
animation-duration: 5s;
animation-timing-function: ease-in-out;
animation-iteration-count: 1;
animation-fill-mode: forwards;
}
@keyframes divMove {
0% {
height: calc(1000px + 100vh);
background-position-x: right;
background-position-y: -50vh;
}
100% {
height: 100vh;
width: 100vw;
background-position: center;
}
}
CSS Keyframes
animation: shine 2s forwards, flicker 3s infinite;
@keyframes blink {
0%,
45%,
75% {
color: $white;
text-shadow: 0 0 0.6rem $white, 0 0 1.5rem #a6a4d6,
-0.2rem 0.1rem 1rem #9692e6, 0.2rem 0.1rem 1rem #8480e9,
0 -0.5rem 2rem #615be1, 0 0.5rem 3rem $purple;
}
28%,
35% {
color: #8480e9;
text-shadow: none;
}
82%,
99% {
color: hsla(234, 77%, 53%, 0.363);
text-shadow: none;
}
}
@keyframes shine {
0% {
color: $darkred;
text-shadow: none;
}
100% {
color: $white;
text-shadow: 0 0 1.1rem $white, 0 0 2rem #a6a4d6,
-0.2rem 0.1rem 1.5rem #9692e6, 0.2rem 0.1rem 1.5rem #8480e9,
0 -0.5rem 2.5rem #615be1, 0 0.5rem 3.5rem $purple;
}
}
Font is aligned to the wall using
- Transform-functions: translate, rotate and skew, to align it with the wall.
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) rotateX(8deg) rotateZ( -2deg) skewY(-0.5deg);
- Perspective Origin, to determine the vanishing point.
perspective-origin: 150% 150%;
On the right wall, there is a flickering light. If you hover over ist, you will see a developer joke from Joke API
Pacman ghosts flying in using CSS Keyframes
Objective: get a reliable lists of popular movies from imdb-api & the fetch the infos for each movie more reliable from the moviedb.org
- Lifecycle-Methode useEffect
- render fetch-calls on first render (ex componentDidMount())
- render fetch-calls if [choosenLang, style, setGameState, phone, render] is changing
-fetch-calls
- fetch popular movies from imdb-api.com
- select one Movie from popluar Movie array
- get a random number
const randomNum = Math.floor(Math.random() * 90);
as index of popular Movie array${movie.items[randomNum].id}
- fetch movie infos and movie poster from themoviedb.org
Obejective: Don't display Movies longer then 25 charaters because of the screen size
-
check lenght of movie title with terenary operator
data.title.length < 25 ? setMovieData(data) : setRender(!render)
if length is more then 25 letters, fetch a new title -
check with media query
phone
the screen-width:import { useMediaPredicate } from "react-media-hook";
ifphone
then movietitle should be < 10;
useEffect(() => {
const getMovie = () => {
fetch(
`https://imdb-api.com/en/API/MostPopularMovies/${process.env.REACT_APP_IMDB_KEY}`
)
.then((res) => res.json())
.then((data) => setMovie(data))
.catch((err) => console.log("Error fetching and parsing data", err));
};
getMovie();
const randomNum = Math.floor(Math.random() * 90);
const getMovieData = () => {
if (!phone) {
if (movie && movie.items.length > 0) {
fetch(
`https://api.themoviedb.org/3/movie/${movie.items[randomNum].id}?api_key=${process.env.REACT_APP_MOVIEDB_KEY}&language=${languages[choosenLang]}`
)
.then((res) => res.json())
.then((data) =>
data.title.length < 25 ? setMovieData(data) : setRender(!render)
);
} else {
fetch(
`https://api.themoviedb.org/3/movie/${items[randomNum].id}?api_key=${process.env.REACT_APP_MOVIEDB_KEY}&language=${languages[choosenLang]}`
)
.then((res) => res.json())
.then((data) =>
data.title.length < 25 ? setMovieData(data) : setRender(!render)
);
}
} else {
if (movie && movie.items.length > 0) {
fetch(
`https://api.themoviedb.org/3/movie/${movie.items[randomNum].id}?api_key=${process.env.REACT_APP_MOVIEDB_KEY}&language=${languages[choosenLang]}`
)
.then((res) => res.json())
.then((data) =>
data.title.length < 10 ? setMovieData(data) : setRender(!render)
);
} else {
fetch(
`https://api.themoviedb.org/3/movie/${items[randomNum].id}?api_key=${process.env.REACT_APP_MOVIEDB_KEY}&language=${languages[choosenLang]}`
)
.then((res) => res.json())
.then((data) =>
data.title.length < 10 ? setMovieData(data) : setRender(!render)
);
}
}
};
getMovieData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [choosenLang, style, setGameState, phone, render]);