commit
4a936358e0
6 changed files with 311 additions and 0 deletions
After Width: | Height: | Size: 5.9 KiB |
File diff suppressed because one or more lines are too long
@ -0,0 +1,86 @@ |
|||
<!DOCTYPE html> |
|||
<html lang="en"> |
|||
|
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|||
<link rel="stylesheet" href="./bootstrap.min.css"> |
|||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css"> |
|||
<link rel="stylesheet" href="./slot.css"> |
|||
<title>След Ботунец...</title> |
|||
<script src="https://unpkg.com/cursor-effects@latest/dist/browser.js"></script> |
|||
<link rel="stylesheet" |
|||
href="https://fonts.googleapis.com/css?family=Russo One"> |
|||
|
|||
<style> |
|||
body { |
|||
background-image: url("./background.gif"); |
|||
background-repeat: repeat; |
|||
background-position: center; |
|||
background-size: cover; |
|||
/* color: rgb(0, 0, 0); */ |
|||
font-family: 'Russo One', sans-serif; |
|||
|
|||
|
|||
} |
|||
.nie-spacing { |
|||
margin-top: 20px; |
|||
margin-bottom: 20px; |
|||
} |
|||
</style> |
|||
|
|||
</head> |
|||
|
|||
<body> |
|||
<div class="container text-center mt-5"> |
|||
<h1 class="display-4">Здравейте!</h1> |
|||
<img src="kayak.png" alt="kayak" class="img-fluid mt-3 nie-spacing"> |
|||
<p class="lead">Ако виждате тази страница, явно сте се наспали и вече не Ви е лошо! </p> |
|||
|
|||
<div class="row mt-5"> |
|||
<div class="col-md-6"> |
|||
<h3>Фото галерия</h3> |
|||
<p>Тук ще бъдат качени снимките и видеата от сватбата в следващите дни <br/> или малко по- натам.. :) <p> |
|||
<a class="btn btn-warning" href="gallery.html">КЛИК!</a> |
|||
</div> |
|||
<div class="col-md-6"> |
|||
<h3>Качване на съдържание</h3> |
|||
<p>Можете да качите Вашите снимки и видеа от тук. Ще бъдат побликувани в галерията вляво :)</p> |
|||
<a class="btn btn-warning" href="upload.html">Клик!</a> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="mt-5"> |
|||
<h2>Допълнение</h2> |
|||
<p>Подготвили сме и томбола с награди. Заслужава си да опитате!</p> |
|||
</div> |
|||
|
|||
<div class="container mt-5"> |
|||
<div class="row justify-content-center"> |
|||
<div class="col-md-6"> |
|||
<div id="game-container"> |
|||
|
|||
<div class="slots"> |
|||
<div class="reel"></div> |
|||
<div class="reel"></div> |
|||
<div class="reel"></div> |
|||
</div> |
|||
|
|||
<div id="debug" class="debug"></div> |
|||
|
|||
|
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/js/bootstrap.bundle.min.js"></script> |
|||
<script > window.addEventListener('load', (event) => { |
|||
new cursoreffects.ghostCursor(); |
|||
}); |
|||
</script> |
|||
<script src="./slot.js"></script> |
|||
</body> |
|||
|
|||
</html> |
After Width: | Height: | Size: 664 KiB |
@ -0,0 +1,123 @@ |
|||
.debug { |
|||
position: fixed; |
|||
top: 4px; |
|||
left: 50%; |
|||
transform: translateX(-50%); |
|||
padding: 4px; |
|||
background: white; |
|||
border: 1px solid rgba(0, 0, 0, 0.3); |
|||
font-family: monospace; |
|||
font-size: 1.6rem; |
|||
} |
|||
|
|||
html, body { |
|||
height: 100%; |
|||
} |
|||
|
|||
body { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
background: linear-gradient(45deg, grey 0%, lightgray 100%); |
|||
} |
|||
|
|||
.slots { |
|||
position: relative; |
|||
width: 276.5px; |
|||
height: 237px; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
padding: 23.7px; |
|||
background: linear-gradient(45deg, grey 0%, lightgray 100%); |
|||
border-top: 1px solid rgba(255, 255, 255, 0.6); |
|||
border-right: 1px solid rgba(255, 255, 255, 0.6); |
|||
border-left: 1px solid rgba(0, 0, 0, 0.4); |
|||
border-bottom: 1px solid rgba(0, 0, 0, 0.4); |
|||
box-shadow: -2px 2px 3px rgba(0, 0, 0, 0.3); |
|||
border-radius: 3px; |
|||
} |
|||
.slots::before, .slots::after { |
|||
content: ""; |
|||
position: absolute; |
|||
top: 50%; |
|||
width: 10px; |
|||
height: 2px; |
|||
background-color: rgba(0, 0, 0, 0.5); |
|||
} |
|||
.slots::before { |
|||
left: 0; |
|||
transform: translate(-200%, -50%); |
|||
} |
|||
.slots::after { |
|||
right: 0; |
|||
transform: translate(200%, -50%); |
|||
} |
|||
@-webkit-keyframes win1 { |
|||
0% { |
|||
background: linear-gradient(45deg, orange 0%, yellow 100%); |
|||
box-shadow: 0 0 80px orange; |
|||
} |
|||
100% { |
|||
background: linear-gradient(45deg, grey 0%, lightgrey 100%); |
|||
box-shadow: -2px 2px 3px rgba(0, 0, 0, 0.3); |
|||
} |
|||
} |
|||
@keyframes win1 { |
|||
0% { |
|||
background: linear-gradient(45deg, orange 0%, yellow 100%); |
|||
box-shadow: 0 0 80px orange; |
|||
} |
|||
100% { |
|||
background: linear-gradient(45deg, grey 0%, lightgrey 100%); |
|||
box-shadow: -2px 2px 3px rgba(0, 0, 0, 0.3); |
|||
} |
|||
} |
|||
@-webkit-keyframes win2 { |
|||
0% { |
|||
background: linear-gradient(45deg, lightblue 0%, lightgreen 100%); |
|||
box-shadow: 0 0 80px lightgreen; |
|||
} |
|||
100% { |
|||
background: linear-gradient(45deg, grey 0%, lightgrey 100%); |
|||
box-shadow: -2px 2px 3px rgba(0, 0, 0, 0.3); |
|||
} |
|||
} |
|||
@keyframes win2 { |
|||
0% { |
|||
background: linear-gradient(45deg, lightblue 0%, lightgreen 100%); |
|||
box-shadow: 0 0 80px lightgreen; |
|||
} |
|||
100% { |
|||
background: linear-gradient(45deg, grey 0%, lightgrey 100%); |
|||
box-shadow: -2px 2px 3px rgba(0, 0, 0, 0.3); |
|||
} |
|||
} |
|||
.slots.win1 { |
|||
-webkit-animation: win1 200ms steps(2, end) infinite; |
|||
animation: win1 200ms steps(2, end) infinite; |
|||
} |
|||
.slots.win2 { |
|||
-webkit-animation: win2 200ms steps(2, end) infinite; |
|||
animation: win2 200ms steps(2, end) infinite; |
|||
} |
|||
.slots .reel { |
|||
position: relative; |
|||
width: 79px; |
|||
height: 237px; |
|||
border: 1px solid rgba(0, 0, 0, 0.3); |
|||
border-radius: 3px; |
|||
overflow: hidden; |
|||
background-image: url(https://assets.codepen.io/439000/slotreel.webp); |
|||
background-position: 0 0; |
|||
background-repeat: repeat-y; |
|||
} |
|||
.slots .reel::after { |
|||
content: ""; |
|||
position: absolute; |
|||
top: 0; |
|||
left: 0; |
|||
width: 100%; |
|||
height: 100%; |
|||
background-image: linear-gradient(rgba(0, 0, 0, 0.4) 0%, transparent 30%, transparent 70%, rgba(0, 0, 0, 0.4) 100%); |
|||
box-shadow: inset 0 0 6px 2px rgba(0, 0, 0, 0.3); |
|||
} |
@ -0,0 +1,91 @@ |
|||
/** |
|||
* Setup |
|||
*/ |
|||
// const debugEl = document.getElementById('debug'),
|
|||
// Mapping of indexes to icons: start from banana in middle of initial position and then upwards
|
|||
iconMap = ["banana", "seven", "cherry", "plum", "orange", "bell", "bar", "lemon", "melon"], |
|||
// Width of the icons
|
|||
icon_width = 79, |
|||
// Height of one icon in the strip
|
|||
icon_height = 79, |
|||
// Number of icons in the strip
|
|||
num_icons = 9, |
|||
// Max-speed in ms for animating one icon down
|
|||
time_per_icon = 100, |
|||
// Holds icon indexes
|
|||
indexes = [0, 0, 0]; |
|||
|
|||
|
|||
/** |
|||
* Roll one reel |
|||
*/ |
|||
const roll = (reel, offset = 0) => { |
|||
// Minimum of 2 + the reel offset rounds
|
|||
const delta = (offset + 2) * num_icons + Math.round(Math.random() * num_icons); |
|||
|
|||
// Return promise so we can wait for all reels to finish
|
|||
return new Promise((resolve, reject) => { |
|||
|
|||
const style = getComputedStyle(reel), |
|||
// Current background position
|
|||
backgroundPositionY = parseFloat(style["background-position-y"]), |
|||
// Target background position
|
|||
targetBackgroundPositionY = backgroundPositionY + delta * icon_height, |
|||
// Normalized background position, for reset
|
|||
normTargetBackgroundPositionY = targetBackgroundPositionY % (num_icons * icon_height); |
|||
|
|||
// Delay animation with timeout, for some reason a delay in the animation property causes stutter
|
|||
setTimeout(() => { |
|||
// Set transition properties ==> https://cubic-bezier.com/#.41,-0.01,.63,1.09
|
|||
reel.style.transition = `background-position-y ${(8 + 1 * delta) * time_per_icon}ms cubic-bezier(.41,-0.01,.63,1.09)`; |
|||
// Set background position
|
|||
reel.style.backgroundPositionY = `${backgroundPositionY + delta * icon_height}px`; |
|||
}, offset * 150); |
|||
|
|||
// After animation
|
|||
setTimeout(() => { |
|||
// Reset position, so that it doesn't get higher without limit
|
|||
reel.style.transition = `none`; |
|||
reel.style.backgroundPositionY = `${normTargetBackgroundPositionY}px`; |
|||
// Resolve this promise
|
|||
resolve(delta % num_icons); |
|||
}, (8 + 1 * delta) * time_per_icon + offset * 150); |
|||
|
|||
}); |
|||
}; |
|||
|
|||
|
|||
/** |
|||
* Roll all reels, when promise resolves roll again |
|||
*/ |
|||
function rollAll() { |
|||
|
|||
// debugEl.textContent = 'rolling...';
|
|||
|
|||
const reelsList = document.querySelectorAll('.slots > .reel'); |
|||
|
|||
Promise |
|||
|
|||
// Activate each reel, must convert NodeList to Array for this with spread operator
|
|||
.all([...reelsList].map((reel, i) => roll(reel, i))) |
|||
|
|||
// When all reels done animating (all promises solve)
|
|||
.then(deltas => { |
|||
// add up indexes
|
|||
deltas.forEach((delta, i) => indexes[i] = (indexes[i] + delta) % num_icons); |
|||
// debugEl.textContent = indexes.map(i => iconMap[i]).join(' - ');
|
|||
|
|||
// Win conditions
|
|||
if (indexes[0] == indexes[1] || indexes[1] == indexes[2]) { |
|||
const winCls = indexes[0] == indexes[2] ? "win2" : "win1"; |
|||
document.querySelector(".slots").classList.add(winCls); |
|||
setTimeout(() => document.querySelector(".slots").classList.remove(winCls), 2000); |
|||
} |
|||
|
|||
// Again!
|
|||
setTimeout(rollAll, 3000); |
|||
}); |
|||
}; |
|||
|
|||
// Kickoff
|
|||
setTimeout(rollAll, 1000); |
Loading…
Reference in new issue