Rotary_Phone / script.js
Pp's picture
Update script.js
8b3189f verified
document.addEventListener('DOMContentLoaded', () => {
const dial = document.getElementById('dial');
const fingerStop = document.getElementById('finger-stop');
const display = document.getElementById('dialed-numbers');
const callButton = document.getElementById('call-button');
const clearButton = document.getElementById('clear-button');
const dialReturnSound = document.getElementById('dial-return-sound');
const dialClickSound = document.getElementById('dial-click-sound');
if (!dialClickSound) console.error("Audio element #dial-click-sound not found!");
if (!dialReturnSound) console.warn("Audio element #dial-return-sound not found (optional).");
const numbers = ['0', '9', '8', '7', '6', '5', '4', '3', '2', '1'];
const numHoles = 10;
// --- Config ---
const holeAngleOffset = -55;
const holeAngleSeparation = 30;
const fingerStopTargetAngle = holeAngleOffset + (numHoles * holeAngleSeparation) + 1;
const dialPlateRadius = dial.offsetWidth / 2;
const holeRadius = dialPlateRadius * 0.72;
const numberPlacementRadius = holeRadius * 0.65; // Inner radius
const DIAL_RETURN_DURATION_MS = 550;
const CLICK_START_DELAY_MS = 50;
// --- State ---
let isDragging = false;
let startAngle = 0;
let currentRotation = 0;
let activeHole = null;
let maxRotationAngle = 0;
let dialedDigit = null;
let holeData = {};
let clickTimer = null;
// --- Generate Holes and Numbers on Dial Plate ---
numbers.forEach((num, index) => {
const angle = holeAngleOffset + index * holeAngleSeparation;
const rad = angle * (Math.PI / 180);
// --- Create Hole ---
const holeX = holeRadius * Math.cos(rad);
const holeY = holeRadius * Math.sin(rad);
const hole = document.createElement('div');
hole.classList.add('hole');
hole.dataset.number = num;
hole.style.transform = `translate(${holeX}px, ${holeY}px)`;
dial.appendChild(hole);
// --- Create Number ---
const numRad = angle * (Math.PI / 180);
const numX = numberPlacementRadius * Math.cos(numRad); // Use inner radius
const numY = numberPlacementRadius * Math.sin(numRad); // Use inner radius
const numElement = document.createElement('div');
numElement.classList.add('dial-plate-number');
numElement.textContent = num;
// 1. Position using left/top relative to dial center
numElement.style.left = `calc(50% + ${numX}px)`;
numElement.style.top = `calc(50% + ${numY}px)`;
// 2. Apply ONLY the centering translation. NO ROTATION.
numElement.style.transform = `translate(-50%, -50%)`; // REMOVED rotate() part
// The numbers will now inherit the rotation of the parent #dial div.
dial.appendChild(numElement);
// Store info
holeData[index] = { element: hole, number: num, startAngle: angle };
// Attach Event Listeners
hole.addEventListener('mousedown', startDrag);
hole.addEventListener('touchstart', startDrag, { passive: false });
});
// --- Position Finger Stop (Same as before) ---
const stopRad = fingerStopTargetAngle * (Math.PI / 180);
const stopRadius = dialPlateRadius * 0.95;
const stopX = stopRadius * Math.cos(stopRad);
const stopY = stopRadius * Math.sin(stopRad);
fingerStop.style.left = `calc(50% + ${stopX}px - 9px)`;
fingerStop.style.top = `calc(50% + ${stopY}px - 22.5px)`;
fingerStop.style.transform = `rotate(${fingerStopTargetAngle + 90}deg)`;
// --- Drag Logic (Remains the same) ---
function startDrag(e) {
if (isDragging) return;
if (clickTimer) clearInterval(clickTimer);
const evt = e.touches ? e.touches[0] : e;
activeHole = evt.target.closest('.hole');
if (!activeHole) return;
const holeInfo = Object.values(holeData).find(data => data.element === activeHole);
if (!holeInfo) return;
isDragging = true;
dialedDigit = holeInfo.number;
activeHole.classList.add('dragging');
dial.style.transition = 'none';
const rect = dial.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
startAngle = getAngle(evt.clientX, evt.clientY, centerX, centerY) - currentRotation;
maxRotationAngle = 0;
document.addEventListener('mousemove', drag);
document.addEventListener('mouseup', endDrag);
document.addEventListener('touchmove', drag, { passive: false });
document.addEventListener('touchend', endDrag);
if (e.touches) e.preventDefault();
}
function drag(e) {
if (!isDragging || !activeHole) return;
const evt = e.touches ? e.touches[0] : e;
const rect = dial.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
let currentMouseAngle = getAngle(evt.clientX, evt.clientY, centerX, centerY);
let potentialRotation = currentMouseAngle - startAngle;
let delta = potentialRotation - currentRotation;
if (delta > 180) delta -= 360;
if (delta < -180) delta += 360;
potentialRotation = currentRotation + delta;
potentialRotation = Math.max(0, potentialRotation);
const holeInfo = Object.values(holeData).find(data => data.element === activeHole);
let holeStartAngle = holeInfo.startAngle;
let maxAllowedRotation = fingerStopTargetAngle - holeStartAngle;
if (maxAllowedRotation < 0) maxAllowedRotation += 360;
potentialRotation = Math.min(potentialRotation, maxAllowedRotation);
currentRotation = potentialRotation;
maxRotationAngle = Math.max(maxRotationAngle, currentRotation);
dial.style.transform = `rotate(${currentRotation}deg)`;
if (e.touches) e.preventDefault();
}
function endDrag() {
if (!isDragging) return;
document.removeEventListener('mousemove', drag);
document.removeEventListener('mouseup', endDrag);
document.removeEventListener('touchmove', drag);
document.removeEventListener('touchend', endDrag);
let numberSuccessfullyDialed = false;
let digitToPlaySoundFor = null;
if (activeHole) {
activeHole.classList.remove('dragging');
const holeInfo = Object.values(holeData).find(data => data.element === activeHole);
const rotationThreshold = 10;
let holeStartAngle = holeInfo.startAngle;
let expectedStopRotation = fingerStopTargetAngle - holeStartAngle;
if (expectedStopRotation < 0) expectedStopRotation += 360;
const tolerance = 2;
if (maxRotationAngle > rotationThreshold && maxRotationAngle >= expectedStopRotation - tolerance) {
appendNumber(dialedDigit);
numberSuccessfullyDialed = true;
digitToPlaySoundFor = dialedDigit;
}
}
dial.style.transition = `transform ${DIAL_RETURN_DURATION_MS / 1000}s cubic-bezier(0.15, 0.85, 0.25, 1)`;
dial.style.transform = 'rotate(0deg)';
if (numberSuccessfullyDialed) {
playDialClickSounds(digitToPlaySoundFor);
}
isDragging = false;
startAngle = 0;
currentRotation = 0;
activeHole = null;
dialedDigit = null;
}
// --- Helper Functions (Remain the same) ---
function getAngle(x, y, centerX, centerY) {
const deltaX = x - centerX;
const deltaY = y - centerY;
let angle = Math.atan2(deltaY, deltaX) * (180 / Math.PI);
return angle;
}
function appendNumber(num) {
if (display.textContent.length < 10) { display.textContent += num; }
}
function clearDisplay() {
display.textContent = '';
if (clickTimer) clearInterval(clickTimer);
}
// --- Sound Playback Logic using <audio> (Remains the same) ---
function playDialClickSounds(digit) {
if (!digit) return;
if (clickTimer) clearInterval(clickTimer);
if (dialReturnSound) {
dialReturnSound.currentTime = 0;
dialReturnSound.play().catch(e => {});
}
if (dialClickSound) {
const numClicks = (digit === '0') ? 10 : parseInt(digit);
if (numClicks <= 0) return;
const durationForClicks = DIAL_RETURN_DURATION_MS - CLICK_START_DELAY_MS;
if (durationForClicks <= 0) { console.error("Duration for clicks too short."); return; }
const clickInterval = durationForClicks / numClicks;
if (clickInterval < 30) { console.warn(`Calculated click interval (${clickInterval.toFixed(1)}ms) is very fast.`); }
let clickCount = 0;
setTimeout(() => {
if (isDragging) return;
clickTimer = setInterval(() => {
if (clickCount < numClicks) {
const clickAudio = document.getElementById('dial-click-sound');
if(clickAudio){
clickAudio.currentTime = 0;
clickAudio.play().catch(e => {});
} else { clearInterval(clickTimer); clickTimer = null; return; }
clickCount++;
} else { clearInterval(clickTimer); clickTimer = null; }
}, Math.max(30, clickInterval));
}, CLICK_START_DELAY_MS);
} else { console.error("Click sound element not found."); }
}
// --- Event Listeners (Remain the same) ---
callButton.addEventListener('click', () => {
const currentNumber = display.textContent;
if (currentNumber) { alert(`Dialing ${currentNumber}...`); }
});
clearButton.addEventListener('click', clearDisplay);
// --- Initial Setup ---
dial.style.transform = 'rotate(0deg)';
console.log("Dialer Initialized (Numbers Inner & Static Orientation).");
});