Hi DW, I'm having an issue with my code, I'm trying to animate the rolling of the ball, I want it to have faces where it will roll to the back and simulate 3D by also revealing the backside now it just display separated but I want it to look like a solid ball rolling.
How can I fix this issue?
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>3D Striped 10-Ball</title>
<style>
body {
margin: 0;
padding: 0;
background-color: #0b6623; /* Billiard table green */
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
overflow: hidden;
perspective: 1000px;
}
/* 3D Scene Wrapper */
.scene {
width: 200px;
height: 200px;
position: relative;
}
/* The Ball Structure */
.ball {
width: 100%;
height: 100%;
position: absolute;
transform-style: preserve-3d;
cursor: grab;
}
.ball:active {
cursor: grabbing;
}
/* Base Sphere Shape using CSS Overlays */
.layer {
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
backface-visibility: visible;
}
/*
The 10-ball has a blue stripe.
We create this using a repeating linear gradient.
*/
.stripe-layer {
background: linear-gradient(
to bottom,
#ffffff 0%, #ffffff 25%,
#0055a5 25%, #0055a5 75%,
#ffffff 75%, #ffffff 100%
);
}
/* Front and Back Number Badges (Faces) */
.badge {
position: absolute;
width: 60px;
height: 60px;
background: #ffffff;
border-radius: 50%;
top: 70px;
left: 70px;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Arial Black', sans-serif;
font-size: 32px;
font-weight: bold;
color: #000000;
box-shadow: inset 0 0 10px rgba(0,0,0,0.2);
backface-visibility: hidden; /* Hides when turned around */
}
/* Position the Front Face */
.face-front {
transform: translateZ(99px);
}
/* Position the Back Face (rotated 180 degrees) */
.face-back {
transform: rotateY(180deg) translateZ(99px);
}
/* Subtle lighting overlay for realistic 3D appearance */
.shading {
background: radial-gradient(circle at 30% 30%, rgba(255,255,255,0.4) 0%, rgba(0,0,0,0.5) 80%);
transform: translateZ(101px);
pointer-events: none;
}
</style>
</head>
<body>
<div class="scene">
<div class="ball" id="poolBall">
<!-- Striped Base -->
<div class="layer stripe-layer"></div>
<!-- Front Face -->
<div class="badge face-front">10</div>
<!-- Back Face -->
<div class="badge face-back">10</div>
<!-- Lighting overlay -->
<div class="layer shading"></div>
</div>
</div>
<script>
const ball = document.getElementById('poolBall');
// Initial rotation coordinates
let targetX = 0;
let targetY = 0;
let currentX = 0;
let currentY = 0;
let isDragging = false;
let previousMousePosition = { x: 0, y: 0 };
// Auto-roll physics when not dragging
let velocityX = 0.5;
let velocityY = 0.8;
// Interaction Listeners
window.addEventListener('mousedown', (e) => {
isDragging = true;
previousMousePosition = { x: e.clientX, y: e.clientY };
});
window.addEventListener('mousemove', (e) => {
if (!isDragging) return;
const deltaX = e.clientX - previousMousePosition.x;
const deltaY = e.clientY - previousMousePosition.y;
// Map mouse movement to 3D matrix rotation speeds
targetY += deltaX * 0.5;
targetX -= deltaY * 0.5;
// Track instant velocity for inertia
velocityX = -deltaY * 0.1;
velocityY = deltaX * 0.1;
previousMousePosition = { x: e.clientX, y: e.clientY };
});
window.addEventListener('mouseup', () => {
isDragging = false;
});
// Touch Support for Mobile
window.addEventListener('touchstart', (e) => {
isDragging = true;
previousMousePosition = { x: e.touches[0].clientX, y: e.touches[0].clientY };
});
window.addEventListener('touchmove', (e) => {
if (!isDragging) return;
const deltaX = e.touches[0].clientX - previousMousePosition.x;
const deltaY = e.touches[0].clientY - previousMousePosition.y;
targetY += deltaX * 0.5;
targetX -= deltaY * 0.5;
velocityX = -deltaY * 0.1;
velocityY = deltaX * 0.1;
previousMousePosition = { x: e.touches[0].clientX, y: e.touches[0].clientY };
});
window.addEventListener('touchend', () => {
isDragging = false;
});
// Smooth Animation Loop
function animate() {
if (!isDragging) {
// Apply constant rolling and friction deceleration over time
targetX += velocityX;
targetY += velocityY;
// Keep the ball slowly rolling infinitely
velocityX *= 0.98;
velocityY *= 0.98;
// Minimum ambient rolling speed
if (Math.abs(velocityX) < 0.1) velocityX = 0.2;
if (Math.abs(velocityY) < 0.1) velocityY = 0.3;
}
// Interpolate values for buttery smooth movement (lerp)
currentX += (targetX - currentX) * 0.1;
currentY += (targetY - currentY) * 0.1;
// Apply 3D matrix transformation to rotate the ball in all directions
ball.style.transform = `rotateX(${currentX}deg) rotateY(${currentY}deg)`;
requestAnimationFrame(animate);
}
// Initialize Animation Loop
animate();
</script>
</body>
</html>