Magic Date Ball 2.0
by scottgriv
HTML
<div class="toy">
<form class="ball">
<div class="window"></div>
<div class="eight">
<span>8</span>
</div>
<div class="answers">
<!-- Affirmatives -->
<input id="affirmative1" type="radio" name="answer" required />
<div class="answer up">It is<br />certain</div>
<input id="affirmative2" type="radio" name="answer" required />
<div class="answer up">It is<br />decidedly<br />so</div>
<input id="affirmative3" type="radio" name="answer" required />
<div class="answer down">Without<br />a<br />doubt</div>
<input id="affirmative4" type="radio" name="answer" required />
<div class="answer up">Yes<br />definitely</div>
<input id="affirmative5" type="radio" name="answer" required />
<div class="answer down">You may<br />rely<br />on it</div>
<input id="affirmative6" type="radio" name="answer" required />
<div class="answer down">As I<br />see it,<br />yes</div>
<input id="affirmative7" type="radio" name="answer" required />
<div class="answer up">Most<br />likely</div>
<input id="affirmative8" type="radio" name="answer" required />
<div class="answer down">Outlook<br />good</div>
<input id="affirmative9" type="radio" name="answer" required />
<div class="answer down">Yes</div>
<input id="affirmative10" type="radio" name="answer" required />
<div class="answer down">Signs<br />point to<br />yes</div>
<!-- Non-comittals -->
<input id="non-committal1" type="radio" name="answer" required />
<div class="answer down">Reply hazy<br />try<br />again</div>
<input id="non-committal2" type="radio" name="answer" required />
<div class="answer up">Ask<br />again<br />later</div>
<input id="non-committal3" type="radio" name="answer" required />
<div class="answer down">Better not<br />tell you<br />now</div>
<input id="non-committal4" type="radio" name="answer" required />
<div...
CSS
body {
margin: 0;
background-color: #222; /* Dark gray background */
display: flex;
justify-content: center;
align-items: center;
height: 100vh; /* Ensures full-page coverage */
overflow: hidden;
}
.toy {
position: relative;
margin: 5vmin auto;
width: 90vmin;
}
.ball {
position: relative;
border-radius: 50%;
width: 90vmin;
height: 90vmin;
overflow: hidden;
background: radial-gradient(
circle at 30vmin 30vmin,
#222 10%,
/* Dark gray center */ #111 40%,
/* Deeper black-gray */ #000 70% /* Almost pure black */
);
box-shadow:
inset 0 0 12vmin rgba(0, 0, 0, 0.9),
0 0 5vmin rgba(0, 0, 0, 0.8); /* Adds depth */
}
/* Answers */
.answers {
width: 90vmin;
height: 90vmin;
}
.answer {
position: absolute;
justify-content: center;
align-items: center;
display: flex;
left: 36vmin;
top: 34.6vmin;
width: 18vmin;
height: 20.8vmin;
color: white;
text-align: center;
font-family: sans-serif;
font-variant: small-caps;
font-size: 2.1vmin;
line-height: 2.4vmin;
opacity: 0;
transition: opacity 1s;
animation: floating 6s linear infinite;
}
/* Triangle */
.answer::before {
content: "";
z-index: -1;
position: absolute;
left: 0;
border-left: 8.8vmin solid transparent;
border-right: 8.8vmin solid transparent;
border-radius: 0.4vmin;
width: 0.4vmin;
height: 0;
}
.answer.up::before {
top: 0.4vmin;
border-bottom: 15.2vmin solid #1c23e8;
}
.answer.down::before {
bottom: 0.4vmin;
border-top: 15.2vmin solid #1c23e8;
}
/* Overlay */
.answer::after {
content: "";
z-index: 1;
position: absolute;
left: -7vmin;
top: -5vmin;
border-radius: 50%;
width: 32vmin;
height: 32vmin;
font-family: sans-serif;
background: linear-gradient(to left, rgba(0, 0, 0, 0.9), rgba(0, 0, 0, 0));
animation: rotating 6s infinite linear;
}
/* Input control */
input {
display: none;
}
input:checked + .answer {
opacity: 1;
transition: opacity 2s 1s;
}
form:active...
JavaScript
window.onload = function () {
var ball = document.querySelector(".ball")
var isPressed = false
var hasActivated = false // Ensures first button press works
ball.addEventListener("click", function () {
const randomDate = generateFutureDate()
triggerAnswerChange(randomDate)
hasActivated = true
})
ball.addEventListener("touchstart", function () {
const randomDate = generateFutureDate()
triggerAnswerChange(randomDate)
hasActivated = true
})
function generateFutureDate() {
const today = new Date()
const currentYear = today.getFullYear()
let futureDate
const start = new Date(today)
start.setDate(today.getDate() + 1) // Start from tomorrow
const end = new Date(currentYear, 11, 31) // Dec 31st
do {
// Calculate a random time between tomorrow and Dec 31st
const timeDiff = end.getTime() - start.getTime()
const randomTimeOffset = Math.floor(Math.random() * timeDiff)
futureDate = new Date(start.getTime() + randomTimeOffset)
// Round to nearest whole day
futureDate.setHours(0, 0, 0, 0)
} while (
futureDate.getDay() === 0 || // Sunday
futureDate.getDay() === 6 // Saturday
)
return formatDate(futureDate)
}
function generateRandomWeekdayInRange(weeksAhead) {
var today = new Date()
var futureDate = new Date(today)
// Move to the start of the next full week (Monday)
futureDate.setDate(today.getDate() + (8 - today.getDay()) + weeksAhead * 7)
// Get a random weekday from Monday to Friday
var randomOffset = getRandomInt(0, 4)
futureDate.setDate(futureDate.getDate() + randomOffset)
return formatDate(futureDate)
}
function formatDate(date) {
var month = date.getMonth() + 1
var day = date.getDate()
var year = date.getFullYear()
return `${padZero(month)}-${padZero(day)}-${year}`
}
function padZero(number) {
return number.toString().padStart(2, "0")
}
function getRandomInt(min,...