Animation added

This commit is contained in:
2025-11-20 23:16:04 -05:00
parent 6d75c8e94e
commit 9197e464a5
8 changed files with 526 additions and 15 deletions

View File

@@ -356,6 +356,75 @@ body {
background-color: var(--neon-cyan);
}
/* Animation Controls */
.animation-controls {
background-color: var(--white);
border: var(--border-thick) solid var(--black);
padding: var(--spacing-md);
margin-bottom: var(--spacing-md);
box-shadow: 6px 6px 0 var(--neon-pink);
}
.control-title {
font-size: 1.2rem;
font-weight: 700;
text-transform: uppercase;
margin-bottom: var(--spacing-md);
letter-spacing: 2px;
}
.control-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-sm);
margin-bottom: var(--spacing-md);
}
.btn-control {
background-color: var(--neon-yellow);
box-shadow: 4px 4px 0 var(--black);
padding: 12px;
}
.speed-slider {
width: 100%;
height: 8px;
background: var(--white);
border: var(--border-medium) solid var(--black);
outline: none;
-webkit-appearance: none;
margin-bottom: 8px;
}
.speed-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 24px;
height: 24px;
background: var(--neon-pink);
border: var(--border-medium) solid var(--black);
cursor: pointer;
box-shadow: 2px 2px 0 var(--black);
}
.speed-slider::-moz-range-thumb {
width: 24px;
height: 24px;
background: var(--neon-pink);
border: var(--border-medium) solid var(--black);
cursor: pointer;
box-shadow: 2px 2px 0 var(--black);
border-radius: 0;
}
.speed-labels {
display: flex;
justify-content: space-between;
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 1px;
}
/* Loading Spinner */
.loading {
display: inline-block;
@@ -370,3 +439,41 @@ body {
@keyframes spin {
to { transform: rotate(360deg); }
}
/* Footer */
.footer {
margin-top: var(--spacing-lg);
background-color: var(--black);
color: var(--white);
border: var(--border-thick) solid var(--black);
padding: var(--spacing-lg);
box-shadow: calc(var(--shadow-offset) * -1) calc(var(--shadow-offset) * -1) 0 var(--neon-cyan);
}
.footer-content {
text-align: center;
}
.copyleft {
font-size: 0.9rem;
font-weight: 700;
letter-spacing: 2px;
margin-bottom: var(--spacing-sm);
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
}
.copyleft-symbol {
font-size: 1.5rem;
display: inline-block;
transform: scaleX(-1);
}
.footer-message {
font-size: 1.1rem;
font-weight: 700;
letter-spacing: 1px;
color: var(--neon-pink);
}

View File

@@ -4,6 +4,15 @@ const API_BASE = '/api';
let currentMazeId = null;
let currentMazeData = null;
let animationState = {
isAnimating: false,
isPaused: false,
currentStep: 0,
visitedCells: [],
solutionPath: [],
animationId: null,
speed: 50 // milliseconds per step
};
// DOM Elements
const generateBtn = document.getElementById('generateBtn');
@@ -15,15 +24,18 @@ const solveDfsBtn = document.getElementById('solveDfsBtn');
const solveBfsBtn = document.getElementById('solveBfsBtn');
const analyzeBtn = document.getElementById('analyzeBtn');
const benchmarkBtn = document.getElementById('benchmarkBtn');
const pauseBtn = document.getElementById('pauseBtn');
const stopBtn = document.getElementById('stopBtn');
const speedSlider = document.getElementById('speedSlider');
const algorithmSelect = document.getElementById('algorithm');
const rowsInput = document.getElementById('rows');
const colsInput = document.getElementById('cols');
const seedInput = document.getElementById('seed');
const solverSelect = document.getElementById('solver');
const resultsContent = document.getElementById('resultsContent');
const mazeInfo = document.getElementById('mazeInfo');
const animationControls = document.getElementById('animationControls');
// Disable buttons initially
function disableActionButtons() {
@@ -56,6 +68,9 @@ solveDfsBtn.addEventListener('click', () => solveMaze('dfs'));
solveBfsBtn.addEventListener('click', () => solveMaze('bfs'));
analyzeBtn.addEventListener('click', analyzeMaze);
benchmarkBtn.addEventListener('click', runBenchmark);
pauseBtn.addEventListener('click', togglePause);
stopBtn.addEventListener('click', stopAnimation);
speedSlider.addEventListener('input', updateSpeed);
// Generate Maze
async function generateMaze() {
@@ -209,13 +224,16 @@ async function loadMazeFile(filename) {
}
}
// Solve Maze
// Solve Maze with Animation
async function solveMaze(algorithm) {
if (currentMazeId === null) {
showError('No maze to solve');
return;
}
// Stop any ongoing animation
stopAnimation();
const btn = algorithm === 'dfs' ? solveDfsBtn : solveBfsBtn;
setLoading(btn, true);
@@ -227,21 +245,141 @@ async function solveMaze(algorithm) {
});
const data = await response.json();
setLoading(btn, false);
if (data.success) {
renderMaze(currentMazeData, data.visited, data.path);
// Start animated solving
startSolvingAnimation(data);
displaySolutionInfo(data);
showSuccess(`Maze solved using ${data.algorithm}!`);
} else {
showError('Failed to solve maze');
}
} catch (error) {
showError('Network error: ' + error.message);
} finally {
setLoading(btn, false);
}
}
// Animation Control Functions
function startSolvingAnimation(solutionData) {
animationState.isAnimating = true;
animationState.isPaused = false;
animationState.currentStep = 0;
animationState.visitedCells = solutionData.visited;
animationState.solutionPath = solutionData.path;
// Show animation controls
animationControls.style.display = 'block';
pauseBtn.textContent = 'PAUSE';
// Disable solve buttons during animation
solveDfsBtn.disabled = true;
solveBfsBtn.disabled = true;
// Start animation
animateStep();
}
function animateStep() {
if (!animationState.isAnimating || animationState.isPaused) {
return;
}
const { currentStep, visitedCells, solutionPath } = animationState;
if (currentStep < visitedCells.length) {
// Show visited cells up to current step
const visibleVisited = visitedCells.slice(0, currentStep + 1);
// Don't show solution path yet, only when all cells are visited
renderMaze(currentMazeData, visibleVisited, null);
animationState.currentStep++;
// Schedule next step
animationState.animationId = setTimeout(animateStep, animationState.speed);
} else if (currentStep < visitedCells.length + solutionPath.length) {
// Now animate the solution path
const pathStep = currentStep - visitedCells.length;
const visiblePath = solutionPath.slice(0, pathStep + 1);
renderMaze(currentMazeData, visitedCells, visiblePath);
animationState.currentStep++;
// Schedule next step (slower for solution path)
animationState.animationId = setTimeout(animateStep, animationState.speed * 2);
} else {
// Animation complete
finishAnimation();
}
}
function togglePause() {
if (!animationState.isAnimating) return;
animationState.isPaused = !animationState.isPaused;
if (animationState.isPaused) {
pauseBtn.textContent = 'RESUME';
if (animationState.animationId) {
clearTimeout(animationState.animationId);
}
} else {
pauseBtn.textContent = 'PAUSE';
animateStep();
}
}
function stopAnimation() {
if (!animationState.isAnimating) return;
animationState.isAnimating = false;
animationState.isPaused = false;
if (animationState.animationId) {
clearTimeout(animationState.animationId);
animationState.animationId = null;
}
// Hide animation controls
animationControls.style.display = 'none';
// Re-enable solve buttons
solveDfsBtn.disabled = false;
solveBfsBtn.disabled = false;
// Show final result if we have solution data
if (animationState.visitedCells.length > 0 && animationState.solutionPath.length > 0) {
renderMaze(currentMazeData, animationState.visitedCells, animationState.solutionPath);
} else {
renderMaze(currentMazeData);
}
}
function finishAnimation() {
animationState.isAnimating = false;
// Hide animation controls
animationControls.style.display = 'none';
// Re-enable solve buttons
solveDfsBtn.disabled = false;
solveBfsBtn.disabled = false;
// Show final result
renderMaze(currentMazeData, animationState.visitedCells, animationState.solutionPath);
showSuccess('Solving animation complete!');
}
function updateSpeed(event) {
// Speed slider: 1 (slow) to 100 (fast)
// Convert to delay: 200ms (slow) to 10ms (fast)
const sliderValue = parseInt(event.target.value);
animationState.speed = 210 - (sliderValue * 2);
}
// Analyze Maze
async function analyzeMaze() {
if (currentMazeId === null) {

View File

@@ -57,14 +57,6 @@
</button>
<!-- Solving Controls -->
<div class="control-group">
<label class="label">SOLVER</label>
<select id="solver" class="select-input">
<option value="dfs">Depth-First Search</option>
<option value="bfs">Breadth-First Search</option>
</select>
</div>
<button id="solveDfsBtn" class="btn btn-secondary">
6. SOLVE (DFS)
</button>
@@ -72,6 +64,23 @@
7. SOLVE (BFS)
</button>
<!-- Animation Controls -->
<div id="animationControls" class="animation-controls" style="display: none;">
<h3 class="control-title">ANIMATION</h3>
<div class="control-buttons">
<button id="pauseBtn" class="btn btn-control">PAUSE</button>
<button id="stopBtn" class="btn btn-control">STOP</button>
</div>
<div class="control-group">
<label class="label">SPEED</label>
<input type="range" id="speedSlider" class="speed-slider" min="1" max="100" value="50">
<div class="speed-labels">
<span>SLOW</span>
<span>FAST</span>
</div>
</div>
</div>
<!-- Action Buttons -->
<button id="visualizeBtn" class="btn btn-accent">
2. VISUALIZE
@@ -112,6 +121,19 @@
</div>
</div>
<!-- Footer -->
<footer class="footer">
<div class="footer-content">
<div class="copyleft">
<span class="copyleft-symbol">🄯</span>
<span>COPYLEFT 2025 - ALL WRONGS RESERVED</span>
</div>
<div class="footer-message">
Made with ❤️ and Python in New Jersey
</div>
</div>
</footer>
<!-- Load Modal -->
<div id="loadModal" class="modal">
<div class="modal-content">