snake: don't repeatedly add inputs to buffer when held; mobile: slide controls

Hunter
Friday, August 1st, 2025 at 4:12 PM

pages/snake/index.html+60-4

pages/snake/index.html
493  			}
494  		}
495  		
496 +		// Track key state to prevent hold repeats
497 +		let keysPressed = {};
498 +		
499  		document.addEventListener('keydown', (event) => {
500  			if (event.code === 'Space') {
501  				if (!gameRunning) {

506  			
507  			if (!gameRunning) return;
508  			
509 +			// Only process if key wasn't already pressed
510 +			if (keysPressed[event.code]) return;
511 +			keysPressed[event.code] = true;
512 +			
513  			switch(event.code) {
514  				case 'ArrowUp':
515  					setDirection({x: 0, y: -1});

526  			}
527  		});
528  		
    -		// Mobile controls
529 +		document.addEventListener('keyup', (event) => {
530 +			keysPressed[event.code] = false;
531 +		});
532 +		
533 +		// Mobile controls - Track which button was last triggered to prevent hold repeats
534 +		let lastTriggeredButton = null;
535 +		
536 +		function handleTouchMove(e) {
537 +			e.preventDefault();
538 +			const touch = e.touches[0];
539 +			const element = document.elementFromPoint(touch.clientX, touch.clientY);
540 +			const button = element?.closest('.control-btn');
541 +			
542 +			if (button && button !== lastTriggeredButton) {
543 +				lastTriggeredButton = button;
544 +				
545 +				if (gameRunning) {
546 +					switch(button.id) {
547 +						case 'upBtn':
548 +							setDirection({x: 0, y: -1});
549 +							break;
550 +						case 'downBtn':
551 +							setDirection({x: 0, y: 1});
552 +							break;
553 +						case 'leftBtn':
554 +							setDirection({x: -1, y: 0});
555 +							break;
556 +						case 'rightBtn':
557 +							setDirection({x: 1, y: 0});
558 +							break;
559 +					}
560 +				} else if (button.id === 'centerBtn') {
561 +					startGame();
562 +				}
563 +			}
564 +		}
565 +		
566 +		function handleTouchEnd(e) {
567 +			e.preventDefault();
568 +			lastTriggeredButton = null;
569 +		}
570 +		
571 +		// Add touch event listeners for sliding behavior
572  		document.getElementById('upBtn').addEventListener('touchstart', (e) => {
573  			e.preventDefault();
574 +			lastTriggeredButton = e.target;
575  			if (gameRunning) setDirection({x: 0, y: -1});
576  		});
577  		document.getElementById('downBtn').addEventListener('touchstart', (e) => {
578  			e.preventDefault();
579 +			lastTriggeredButton = e.target;
580  			if (gameRunning) setDirection({x: 0, y: 1});
581  		});
582  		document.getElementById('leftBtn').addEventListener('touchstart', (e) => {
583  			e.preventDefault();
584 +			lastTriggeredButton = e.target;
585  			if (gameRunning) setDirection({x: -1, y: 0});
586  		});
587  		document.getElementById('rightBtn').addEventListener('touchstart', (e) => {
588  			e.preventDefault();
589 +			lastTriggeredButton = e.target;
590  			if (gameRunning) setDirection({x: 1, y: 0});
591  		});
592  		document.getElementById('centerBtn').addEventListener('touchstart', (e) => {
593  			e.preventDefault();
    -			if (!gameRunning) {
    -				startGame();
    -			}
594 +			lastTriggeredButton = e.target;
595 +			if (!gameRunning) startGame();
596  		});
597  		
598 +		// Add global touch move and end listeners for sliding
599 +		document.addEventListener('touchmove', handleTouchMove);
600 +		document.addEventListener('touchend', handleTouchEnd);
601 +		
602  		// Handle window resize and orientation changes
603  		window.addEventListener('resize', () => {
604  			updateBoardSize();