<div class="container">
<header class="">
<div class="">
<h1 id="title">Met Guess</h1>
</div>
<p id="about-the-game" class="">
<!-- <span class="large">The game to test your knowlege of the Met Collection</span> -->
<p style="background-color: red; padding: 10px;font-size: 120%">N.B. This is not the complete version of Met Guess, it is an example codepen to illustrate loading times and custom loading animation</p>
</p>
<p><a href="https://github.com/hariseldon27/met-museum-game" targer="_blank">Find the more or less finished game on GitHub here:
https://github.com/hariseldon27/met-museum-game</a></p>
</header>
<div class="mx-auto"><!--container for the card-->
<div id="card" class="">
<div id="card-image-holder">
<img src="https://images.metmuseum.org/CRDImages/eg/original/DT563.jpg" id="main-image">
</div><!--end card image holder-->
<div id="gamepiece-holder" class="">
<ul id="game-pieces" class="">
<li class="game-option" id="objectID-1">Option 1</li>
<li class="game-option" id="objectID-2">Option 2</li>
<li class="game-option" id="objectID-3">Option 3</li>
</ul><!--end of game choices-->
</div>
<div id="game-info-controls">
<div id="gamestats" class="">
Score: <span id="total-correct">0</span> correct /
<span id="total-questions">0</span> tries
<!-- <span class="" id="timeleft">Time Left: XX:XX</span> -->
</div>
<div class="gap-2 d-md-block" id="controls-holder">
<!-- <div id="score-skip" class=""> -->
<button id="skip" class="btn new-game-button btn-outline-dark">Next >></button>
<!-- </div> -->
<button role="button" id="new-game" type="button" class="btn new-game-button btn-outline-dark" data-bs-toggle="modal">
New Game!
</button>
</div>
</div>
<!-- <button role="button" type="button" class="btn btn" data-bs-toggle="modal" data-bs-target="#incorrectgit-popup">open wrong answ modal</button> -->
</div>
<!--end of card-->
</div><!--end of card-->
<div id="animated-loader" class="" style="">
<svg id="mdot" data-name="m-dot" xmlns="http://www.w3.org/2000/svg" viewBox="-180 0 1200 1200">
<path fill="white" stroke="none" d="M665.44,304.14v177.7q0,76.68,4.66,104.62t13.26,35.11q16.47,13.62,52.31,13.61l1.43,8.6h-192l1.43-8.6q35.1,0,51.59-12.89,13.6-10.75,15.76-70.22,1.44-23.66,1.44-70.23V178.75L414.66,636.62,207.57,249V480.41q0,71.65,6.81,103.54t21.86,41.56q15,9.68,50.15,9.67l1.44,8.6H111.56l1.43-8.6q26.51,0,39.77-3.94t22.57-21.14q9.3-17.19,11.82-45.85t2.51-83.84V304.86q0-55.17-2.51-83.84t-11.82-45.86Q166,158,152.76,154T113,150.09l-1.43-7.89h96L429,560,611.7,142.2H737.1l-1.43,7.89q-35.85,0-52.31,13.61Q665.44,178.75,665.44,304.14Z" />
<path fill="none" stroke="black" d="M665.44,304.14v177.7q0,76.68,4.66,104.62t13.26,35.11q16.47,13.62,52.31,13.61l1.43,8.6h-192l1.43-8.6q35.1,0,51.59-12.89,13.6-10.75,15.76-70.22,1.44-23.66,1.44-70.23V178.75L414.66,636.62,207.57,249V480.41q0,71.65,6.81,103.54t21.86,41.56q15,9.68,50.15,9.67l1.44,8.6H111.56l1.43-8.6q26.51,0,39.77-3.94t22.57-21.14q9.3-17.19,11.82-45.85t2.51-83.84V304.86q0-55.17-2.51-83.84t-11.82-45.86Q166,158,152.76,154T113,150.09l-1.43-7.89h96L429,560,611.7,142.2H737.1l-1.43,7.89q-35.85,0-52.31,13.61Q665.44,178.75,665.44,304.14Z" />
<circle r="45" fill="red" >
<animateMotion dur="8s" repeatCount="indefinite" path="M665.44,304.14v177.7q0,76.68,4.66,104.62t13.26,35.11q16.47,13.62,52.31,13.61l1.43,8.6h-192l1.43-8.6q35.1,0,51.59-12.89,13.6-10.75,15.76-70.22,1.44-23.66,1.44-70.23V178.75L414.66,636.62,207.57,249V480.41q0,71.65,6.81,103.54t21.86,41.56q15,9.68,50.15,9.67l1.44,8.6H111.56l1.43-8.6q26.51,0,39.77-3.94t22.57-21.14q9.3-17.19,11.82-45.85t2.51-83.84V304.86q0-55.17-2.51-83.84t-11.82-45.86Q166,158,152.76,154T113,150.09l-1.43-7.89h96L429,560,611.7,142.2H737.1l-1.43,7.89q-35.85,0-52.31,13.61Q665.44,178.75,665.44,304.14Z" />
</circle>
</svg>
<div class="loading" id="loading"></div>
</div>
</div> <!--end of our main body row-->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
html * {
text-align: center;
}
.tiny {
font-size: 50%;
}
.gigantic {
font-size: 200%
}
html {
position: relative;
min-height: 100%;
}
body {
margin: 0 0 40px;
}
/* media queries up top yo */
@media (max-width: 1000px){
div#redheader {
height:40px;
}
.tiny {
font-size:45%;
}
.gigantic {
font-size:120%
}
#cheekynote {
font-size:70%
}
.footerleft{
display: none;
}
#main-image,
#correct-modal-object-image,
#incorrect-modal-object-image {
width: 20em;
}
}
/* end large breakpoint */
@media (max-width: 768px){
#met-header {
display: none;
}
span#met-header-short {
display: inline;
}
footer.footer span {
display: none;
}
}
/* end medium breakpoint */
@media (max-width: 425px){
div#redheader {
height:30px;
}
.tiny {
font-size:42%;
}
.gigantic {
font-size:70%
}
#cheekynote {
font-size:60%
}
img#welcome-image {
width: 23vh;
}
#main-image,
#correct-modal-object-image,
#incorrect-modal-object-image {
width: 15em;
}
}
/* end small breakpoint */
/* main button stylez */
button#incorrect-modal-next-question-button,
button#correct-modal-next-question-button,
button#start-game-button,
button.next-button,
button.new-game-button {
background: none;
width: 12em;
border: black solid 2px;
padding: 7px;
color: black;
}
button#incorrect-modal-next-question-button:hover,
button#correct-modal-next-question-button:hover,
button#start-game-button:hover,
button.next-button:hover,
button.new-game-button:hover,
li.game-option:hover {
background-color: #e03;
transition: 0.3s;
border-style:inset;
border-color:transparent;
color: white;
}
/* getting into the meat of it */
ul#game-pieces {
display: inline-block;
list-style: none;
list-style-type: none;
position:relative;
float: none;
text-align: center;
padding: 0;
margin: 0;
}
li.game-option {
padding: 10px;
position: relative;
background: transparent;
border: black solid 2px;
border-radius: 4px;
margin: 5px 5px 0px 5px;
list-style: none;
}
li.game-option:hover {
color: white;
}
#about-the-game {
max-width: 40vh;
position: relative;
margin: auto;
}
#main-image {
max-width: 40wh;
max-height: 40vh;
padding: 0 0 16 px 0;
}
#game-info-controls {
position: relative;
display: block;
}
#gamestats {
background: none;
border: black solid 2px;
padding: 10px 15px 10px 15px ;
color: black;
margin: 5px;
display: inline-block;
border-radius: 40px;
background: black;
color: white;
}
.metred,
a.btn.metred {
background: #e03;
border-color: #e45;
}
a.btn.metred:hover {
background: #e45;
transition: 0.3s;
}
#redheader {
height: 70px;
background: #e03;
z-index: 3000;
color: white;
text-align: left;
font-size: x-large;
margin-bottom: 25px;
}
#redheader p {
text-align: left;
padding: 0 15px 0;
}
#gamestats {
margin: 0.5vh;
}
#title,
#met-header,
#met-header-short,
#welcome-card h4 {
font-family: "Gilda Display", serif;
}
#met-header-short {
display: none;
}
#indemnification {
color: #444;
}
#card {
padding: 18px 0 5px 0;
}
footer.footer {
size: 2em;
background: black;
display: inline-block;
left: 0;
bottom: 0;
width: 100%;
height: 40px;
position: absolute;
bottom: 0;
color: white;
font-size: .8em
}
footer span{
padding: 8px
}
span.footerleft {
float: left;
}
span.footerright {
float: right;
}
/* modal windows */
#welcome-image {
width: 40vh;
}
#modal-object-image {
width: 30vh;
}
#welcome-popup {
background: #666;
}
#incorrectModalObjectDetails *,
#correctModalObjectDetails * {
text-align: left;
}
#correct-popup h6,
#incorrect-popup h6 {
background: #999;
}
#animated-loader{
z-index: 997;
position: fixed;
z-index: 996;
height: 20em;
width: 20em;
overflow: show;
margin: auto;
top: 0;
left: 0;
bottom: 0;
right: 0;
transition: 0.3s;
display: none;
}
#loading {
position: fixed;
z-index: 996;
height: 20em;
width: 20em;
overflow: show;
margin: auto;
top: 0;
left: 0;
bottom: 0;
right: 0;
}
/* Transparent Overlay */
#loading:after {
content: '';
display: block;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.363);
}
window.addEventListener("DOMContentLoaded", (event) => {
console.log("DOM fully loaded and parsed");
init();
});
//document selectors
const mainImage = document.getElementById("main-image");
const option1 = document.getElementById("objectID-1");
const option2 = document.getElementById("objectID-2");
const option3 = document.getElementById("objectID-3");
const nextButton = document.getElementById("skip");
const scoreEl = document.getElementById("scorekeeper");
const timeLeftEl = document.getElementById("timeleft");
const welcomePopupEl = document.getElementById("modal-holder");
const totalCorrectScore = document.getElementById("total-correct");
const totalQuestionsScore = document.getElementById("total-questions");
const newGameButton = document.getElementById("new-game");
//event listeners
option1.addEventListener("click", winLogic);
option2.addEventListener("click", winLogic);
option3.addEventListener("click", winLogic);
newGameButton.addEventListener("click", function (e) {
e.preventDefault();
reset();
totalQuestionsScore.textContent = 0;
totalCorrectScore.textContent = 0;
correctAnswers = 0;
totalQuestions = 0;
});
nextButton.addEventListener("click", function (e) {
e.preventDefault();
reset();
totalQuestions++;
totalQuestionsScore.textContent = totalQuestions;
});
let correctAnswers = 0;
let totalQuestions = 0;
function init() {
goSearch();
}
const baseSearchParam =
"https://collectionapi.metmuseum.org/public/collection/v1/search?medium=Paintings&q=cat&department=13";
//returned value is no lower than (and may possibly equal) min, and is less than (and not equal) max.
function randomNum(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
function goSearch(searchTerm) {
fetch(baseSearchParam)
.then((res) => res.json())
.then((data) => searchResults(data));
}
//this is the object that will store our game options
//first the objectID is filled from picking three random numbers from our search-set
//then the artist value should be filled in by a second fetch call to each of the relevant objects
const gameOptions = {
option1: {
objectID: 01,
artist: "a",
title: "title",
year: 0000,
image: "url",
},
option2: {
objectID: 02,
artist: "b",
title: "title",
year: 0000,
image: "url",
},
option3: {
objectID: 03,
artist: "c",
title: "title",
year: 0000,
image: "url",
},
option4: {
objectID: 04,
artist: "d",
title: "title",
year: 0000,
image: "url",
},
option5: {
objectID: 05,
artist: "e",
title: "title",
year: 0000,
image: "url",
},
};
//this is the object that will store our game options
//first the objectID is filled from picking three random numbers from our search-set
//then the artist value should be filled in by a second fetch call to each of the relevant objects
//build function that loops over gameOptions to check for blank strings
function checkMissingStringsInObject() {
//console.log(gameOptions);
for (const option in gameOptions) {
if (gameOptions[option].artist === "") {
gameOptions[option] = gameOptions.option4;
} else if (gameOptions[option].artist === "Unidentified artist") {
gameOptions[option] = gameOptions.option5;
}
}
//console.log(gameOptions);
}
async function searchResults(data) {
showSpinner()
//this fills in our game pieces with randomly chosen objects
for (let option in gameOptions) {
gameOptions[option].objectID = data.objectIDs[randomNum(1, 800)];
let objectToFind = gameOptions[option].objectID;
//use our three objectIDs to then search for each object and gather data
await fetch(
`https://collectionapi.metmuseum.org/public/collection/v1/objects/${objectToFind}`
)
.then((resp) => resp.json())
//next we set the artist name in our local object based on the info from the api
.then((data) => {
// this points to the gameOptions object, and for each option.artist in the object
//it will set that param to data.artistDisplayName as it comes from the api
gameOptions[option].artist = data.artistDisplayName;
gameOptions[option].image = data.primaryImageSmall;
gameOptions[option].title = data.title;
gameOptions[option].year = data.objectEndDate;
});
}
//run logic to check for empty strings
checkMissingStringsInObject();
initialLoad();
}
function randomizeWinnerOptions() {
const randNum = Math.floor(Math.random() * (6 - 1) + 1);
// console.log(randNum);
for (let option in gameOptions) {
if (option === `option${randNum}`) {
gameOptions["correct"] = gameOptions[option];
}
}
}
// initialLoad populates DOM
async function initialLoad() {
//set the id of each of the board pieces to match the input objectid
option1.setAttribute("data-id", gameOptions.option1.objectID);
option2.setAttribute("data-id", gameOptions.option2.objectID);
option3.setAttribute("data-id", gameOptions.option3.objectID);
//set the text on the game pieces
option1.textContent = gameOptions.option1.artist;
option2.textContent = gameOptions.option2.artist;
option3.textContent = gameOptions.option3.artist;
//below randomly chooses from the three existing options to choose a winner and make a new obj
randomizeWinnerOptions();
//the below attribute won't work if we don't fire randomizeWinnerOptions first
mainImage.setAttribute("data-id", gameOptions.correct.objectID);
mainImage.src = gameOptions.correct.image;
hideSpinner()
}
// winner logic- add to event listeners on option buttons
function winLogic() {
//see if data-id matches mainImage.dat-id
if (this.getAttribute("data-id") === mainImage.getAttribute("data-id")) {
console.log("correct!");
correctAnswers++;
totalQuestions++;
totalCorrectScore.textContent = correctAnswers;
totalQuestionsScore.textContent = totalQuestions;
// openModalWindow()
reset();
} else {
console.log("wrong!");
totalQuestions++;
totalQuestionsScore.textContent = totalQuestions;
// openModalWindow()
reset();
}
}
//reset the game board (currently just clears, will need to fire goSearch again)
const reset = () => {
option1.setAttribute("data-id", "");
option2.setAttribute("data-id", "");
option3.setAttribute("data-id", "");
mainImage.setAttribute("data-id", "");
goSearch();
};
// animated popup
const loadingEl = document.getElementById('animated-loader')
function showSpinner() {
loadingEl.style.display = "block";
console.log("showspinner")
}
function hideSpinner() {
loadingEl.style.display = "none";
console.log("hidespinner")
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.