personal-space / index.html
devinyonas's picture
Add 2 files
8f0ee54 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Minimalist 3D Dice Roller</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--primary-color: #2c3e50;
--secondary-color: #e74c3c;
--background-color: #f5f6fa;
--dice-color: #ffffff;
--shadow-color: rgba(0, 0, 0, 0.2);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: var(--background-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
color: var(--primary-color);
overflow-x: hidden;
}
.container {
text-align: center;
max-width: 800px;
padding: 2rem;
}
h1 {
font-weight: 300;
margin-bottom: 2rem;
font-size: 2.5rem;
letter-spacing: 1px;
}
.controls {
display: flex;
justify-content: center;
align-items: center;
margin-bottom: 2rem;
gap: 1rem;
flex-wrap: wrap;
}
.dice-count {
display: flex;
align-items: center;
gap: 0.5rem;
}
label {
font-size: 1.1rem;
}
input[type="number"] {
width: 60px;
padding: 0.5rem;
border: 1px solid #ddd;
border-radius: 4px;
text-align: center;
font-size: 1rem;
outline: none;
transition: border 0.3s;
}
input[type="number"]:focus {
border-color: var(--secondary-color);
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
.btn {
background-color: var(--secondary-color);
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
transition: all 0.3s;
display: flex;
align-items: center;
gap: 0.5rem;
}
.btn:hover {
background-color: #c0392b;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.btn:active {
transform: translateY(0);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.dice-container {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 2rem;
margin: 2rem 0;
min-height: 200px;
perspective: 1000px;
}
.dice {
width: 80px;
height: 80px;
position: relative;
transform-style: preserve-3d;
transition: transform 1s ease-in-out;
cursor: pointer;
}
@media (max-width: 768px) {
.dice {
width: 60px;
height: 60px;
}
}
.dice-face {
position: absolute;
width: 100%;
height: 100%;
background-color: var(--dice-color);
border-radius: 8px;
display: flex;
justify-content: center;
align-items: center;
box-shadow: inset 0 0 10px var(--shadow-color);
border: 1px solid #eee;
}
.dot {
width: 12px;
height: 12px;
background-color: var(--primary-color);
border-radius: 50%;
position: absolute;
}
@media (max-width: 768px) {
.dot {
width: 10px;
height: 10px;
}
}
/* Dice face positions */
.front { transform: translateZ(40px); }
.back { transform: rotateY(180deg) translateZ(40px); }
.right { transform: rotateY(90deg) translateZ(40px); }
.left { transform: rotateY(-90deg) translateZ(40px); }
.top { transform: rotateX(90deg) translateZ(40px); }
.bottom { transform: rotateX(-90deg) translateZ(40px); }
/* Media query for smaller dice on mobile */
@media (max-width: 768px) {
.front { transform: translateZ(30px); }
.back { transform: rotateY(180deg) translateZ(30px); }
.right { transform: rotateY(90deg) translateZ(30px); }
.left { transform: rotateY(-90deg) translateZ(30px); }
.top { transform: rotateX(90deg) translateZ(30px); }
.bottom { transform: rotateX(-90deg) translateZ(30px); }
}
/* Dot positions for each face */
/* All faces use these positions and visibility is controlled by JavaScript */
.dot-center { transform: translate(0, 0); }
.dot-top-left {
top: 20%;
left: 20%;
}
.dot-top-right {
top: 20%;
right: 20%;
}
.dot-middle-left {
left: 20%;
}
.dot-middle-right {
right: 20%;
}
.dot-bottom-left {
bottom: 20%;
left: 20%;
}
.dot-bottom-right {
bottom: 20%;
right: 20%;
}
/* Dice roll animations */
@keyframes roll-twist {
0% { transform: rotateX(0) rotateY(0) rotateZ(0); }
20% { transform: rotateX(180deg) rotateY(360deg) rotateZ(45deg); }
40% { transform: rotateX(360deg) rotateY(180deg) rotateZ(90deg); }
60% { transform: rotateX(540deg) rotateY(360deg) rotateZ(135deg); }
80% { transform: rotateX(720deg) rotateY(540deg) rotateZ(180deg); }
100% { transform: rotateX(900deg) rotateY(720deg) rotateZ(225deg); }
}
@keyframes roll-flip {
0% { transform: rotateX(0) rotateY(0); }
30% { transform: rotateX(360deg) rotateY(180deg); }
70% { transform: rotateX(720deg) rotateY(540deg); }
100% { transform: rotateX(1080deg) rotateY(720deg); }
}
@keyframes roll-spiral {
0% { transform: rotateX(0) rotateY(0) rotateZ(0) translateY(0); }
20% { transform: rotateX(180deg) rotateY(90deg) rotateZ(90deg) translateY(-50px); }
40% { transform: rotateX(360deg) rotateY(180deg) rotateZ(180deg) translateY(0); }
60% { transform: rotateX(540deg) rotateY(270deg) rotateZ(270deg) translateY(30px); }
80% { transform: rotateX(720deg) rotateY(360deg) rotateZ(360deg) translateY(-20px); }
100% { transform: rotateX(900deg) rotateY(450deg) rotateZ(450deg) translateY(0); }
}
@keyframes roll-bounce {
0% { transform: rotateX(0) rotateY(0) translateY(0); }
10% { transform: rotateX(180deg) rotateY(90deg) translateY(-40px); }
25% { transform: rotateX(360deg) rotateY(180deg) translateY(0); }
35% { transform: rotateX(540deg) rotateY(270deg) translateY(-30px); }
45% { transform: rotateX(720deg) rotateY(360deg) translateY(0); }
55% { transform: rotateX(900deg) rotateY(450deg) translateY(-15px); }
65% { transform: rotateX(1080deg) rotateY(540deg) translateY(0); }
75% { transform: rotateX(1260deg) rotateY(630deg) translateY(-5px); }
85% { transform: rotateX(1440deg) rotateY(720deg) translateY(0); }
100% { transform: rotateX(1620deg) rotateY(810deg) translateY(0); }
}
.rolling-twist {
animation: roll-twist 1.5s cubic-bezier(0.17, 0.67, 0.83, 0.67) forwards;
}
.rolling-flip {
animation: roll-flip 1.5s cubic-bezier(0.17, 0.67, 0.83, 0.67) forwards;
}
.rolling-spiral {
animation: roll-spiral 1.5s cubic-bezier(0.17, 0.67, 0.83, 0.67) forwards;
}
.rolling-bounce {
animation: roll-bounce 2s cubic-bezier(0.17, 0.67, 0.83, 0.67) forwards;
}
.total-display {
margin-top: 2rem;
font-size: 1.5rem;
opacity: 0;
transition: opacity 0.5s;
}
.show-total {
opacity: 1;
}
footer {
margin-top: 3rem;
font-size: 0.9rem;
color: #7f8c8d;
}
/* Responsive adjustments */
@media (max-width: 600px) {
h1 {
font-size: 2rem;
}
.controls {
flex-direction: column;
}
.dice-count {
margin-bottom: 1rem;
}
}
</style>
</head>
<body>
<div class="container">
<h1>Minimalist Dice Roller</h1>
<div class="controls">
<div class="dice-count">
<label for="dice-number">Number of dice:</label>
<input type="number" id="dice-number" min="1" max="10" value="3">
</div>
<button id="roll-btn" class="btn">
<i class="fas fa-dice"></i> Roll Dice
</button>
</div>
<div id="dice-container" class="dice-container"></div>
<div id="total-display" class="total-display"></div>
<footer>
Click any die to roll it individually
</footer>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const diceContainer = document.getElementById('dice-container');
const rollBtn = document.getElementById('roll-btn');
const diceNumberInput = document.getElementById('dice-number');
const totalDisplay = document.getElementById('total-display');
let diceCount = 3;
let diceElements = [];
// Possible roll animations
const rollAnimations = [
'rolling-twist',
'rolling-flip',
'rolling-spiral',
'rolling-bounce'
];
// Create initial dice
initializeDice();
// Update dice count when input changes
diceNumberInput.addEventListener('change', function() {
const value = parseInt(this.value);
if (isNaN(value) || value < 1) {
this.value = 1;
diceCount = 1;
} else if (value > 10) {
this.value = 10;
diceCount = 10;
} else {
diceCount = value;
}
initializeDice();
});
// Roll all dice
rollBtn.addEventListener('click', function() {
rollAllDice();
});
function initializeDice() {
// Clear existing dice
diceContainer.innerHTML = '';
diceElements = [];
// Create new dice
for (let i = 0; i < diceCount; i++) {
const dice = createDiceElement();
diceContainer.appendChild(dice);
diceElements.push(dice);
}
}
function createDiceElement() {
const dice = document.createElement('div');
dice.className = 'dice';
// Create all 6 faces of the dice with ALL possible dots
const faces = [
{ class: 'front', value: 1 },
{ class: 'back', value: 2 },
{ class: 'right', value: 3 },
{ class: 'left', value: 4 },
{ class: 'top', value: 5 },
{ class: 'bottom', value: 6 }
];
faces.forEach((face) => {
const faceElement = document.createElement('div');
faceElement.className = `dice-face ${face.class}`;
// Create all possible dots (they will be shown/hidden based on value)
const dotPositions = [
'dot-center',
'dot-top-left',
'dot-top-right',
'dot-middle-left',
'dot-middle-right',
'dot-bottom-left',
'dot-bottom-right'
];
// Create all dots (visibility will be controlled by JS)
dotPositions.forEach(pos => {
const dot = document.createElement('div');
dot.className = `dot ${pos}`;
faceElement.appendChild(dot);
});
dice.appendChild(faceElement);
});
// Show random face initially
rollSingleDice(dice, false);
// Add click event to roll single die
dice.addEventListener('click', function() {
rollSingleDice(this, true);
updateTotal();
});
return dice;
}
function rollSingleDice(diceElement, withAnimation = true) {
// Remove any existing animation classes
rollAnimations.forEach(anim => {
diceElement.classList.remove(anim);
});
if (withAnimation) {
// Randomly select an animation
const randomAnim = rollAnimations[Math.floor(Math.random() * rollAnimations.length)];
diceElement.classList.add(randomAnim);
// Remove animation class after animation completes
setTimeout(() => {
diceElement.className = 'dice'; // Reset all classes
showRandomFace(diceElement);
}, 2000);
} else {
showRandomFace(diceElement);
}
}
function showRandomFace(diceElement) {
// Generate random number between 1 and 6
const randomValue = Math.floor(Math.random() * 6) + 1;
// Calculate rotation to show the correct face
const rotations = [
{ x: 0, y: 0 }, // 1 (front)
{ x: 0, y: 180 }, // 2 (back)
{ x: 0, y: 90 }, // 3 (right)
{ x: 0, y: 270 }, // 4 (left)
{ x: 270, y: 0 }, // 5 (top)
{ x: 90, y: 0 } // 6 (bottom)
];
const rotation = rotations[randomValue - 1];
diceElement.style.transform = `rotateX(${rotation.x}deg) rotateY(${rotation.y}deg)`;
diceElement.setAttribute('data-value', randomValue);
// Update dots visibility for all faces based on their values
const faces = diceElement.querySelectorAll('.dice-face');
faces.forEach((face, idx) => {
const value = idx + 1; // faces are in order 1-6
const dots = face.querySelectorAll('.dot');
// Hide all dots first
dots.forEach(dot => dot.style.display = 'none');
// Show dots based on face value
switch(value) {
case 1: // Center
dots[0].style.display = 'block';
break;
case 2: // Top-left, Bottom-right
dots[1].style.display = 'block';
dots[6].style.display = 'block';
break;
case 3: // Center + case 2
dots[0].style.display = 'block';
dots[1].style.display = 'block';
dots[6].style.display = 'block';
break;
case 4: // All corners
dots[1].style.display = 'block';
dots[2].style.display = 'block';
dots[5].style.display = 'block';
dots[6].style.display = 'block';
break;
case 5: // Center + case 4
dots[0].style.display = 'block';
dots[1].style.display = 'block';
dots[2].style.display = 'block';
dots[5].style.display = 'block';
dots[6].style.display = 'block';
break;
case 6: // All corners + middle
dots[1].style.display = 'block';
dots[2].style.display = 'block';
dots[3].style.display = 'block';
dots[4].style.display = 'block';
dots[5].style.display = 'block';
dots[6].style.display = 'block';
break;
}
});
}
function rollAllDice() {
// Hide total display during roll
totalDisplay.classList.remove('show-total');
// Roll each die with a slight delay between them
diceElements.forEach((dice, index) => {
setTimeout(() => {
rollSingleDice(dice, true);
}, index * 150);
});
// Show total after all dice finish rolling
setTimeout(() => {
updateTotal();
totalDisplay.classList.add('show-total');
}, 2500);
}
function updateTotal() {
let total = 0;
let allRolled = true;
diceElements.forEach(dice => {
const value = dice.getAttribute('data-value');
if (value) {
total += parseInt(value);
} else {
allRolled = false;
}
});
if (allRolled) {
totalDisplay.textContent = `Total: ${total}`;
} else {
totalDisplay.textContent = '';
}
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body>
</html>