Files
swiper/script.js

345 lines
12 KiB
JavaScript

document.addEventListener('DOMContentLoaded', function() {
const card = document.getElementById('current-card');
const lastActionText = document.getElementById('last-action');
const leftHint = document.querySelector('.left-hint');
const rightHint = document.querySelector('.right-hint');
const upHint = document.querySelector('.up-hint');
const downHint = document.querySelector('.down-hint');
// Modal elements
const modal = document.getElementById('fullscreen-modal');
const fullscreenImage = document.getElementById('fullscreen-image');
const closeModal = document.querySelector('.close-modal');
const modalResolution = document.getElementById('modal-resolution');
const modalFilename = document.getElementById('modal-filename');
const modalCreationDate = document.getElementById('modal-creation-date');
// Current image information
let currentImageInfo = null;
// Button event listeners
document.getElementById('btn-left').addEventListener('click', () => performSwipe('left'));
document.getElementById('btn-right').addEventListener('click', () => performSwipe('right'));
document.getElementById('btn-up').addEventListener('click', () => performSwipe('up'));
document.getElementById('btn-down').addEventListener('click', () => performSwipe('down'));
// Touch start time for distinguishing between swipe and tap
let touchStartTime = 0;
// Touch variables
let startX, startY, moveX, moveY;
let isDragging = false;
const swipeThreshold = 100; // Minimum distance for a swipe to be registered
// Touch event handlers
card.addEventListener('touchstart', handleTouchStart, false);
card.addEventListener('touchmove', handleTouchMove, false);
card.addEventListener('touchend', handleTouchEnd, false);
// Mouse event handlers (for desktop testing)
card.addEventListener('mousedown', handleMouseDown, false);
document.addEventListener('mousemove', handleMouseMove, false);
document.addEventListener('mouseup', handleMouseUp, false);
// Click handler for viewing full-resolution image
card.addEventListener('click', handleCardClick);
// Close modal when clicking the close button
closeModal.addEventListener('click', () => {
modal.style.display = 'none';
});
// Close modal when clicking outside the image
window.addEventListener('click', (e) => {
if (e.target === modal) {
modal.style.display = 'none';
}
});
// Close modal with escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && modal.style.display === 'block') {
modal.style.display = 'none';
}
});
function handleTouchStart(e) {
startX = e.touches[0].clientX;
startY = e.touches[0].clientY;
isDragging = true;
card.classList.add('swiping');
// Store touch start time to differentiate between swipe and tap
touchStartTime = new Date().getTime();
}
function handleTouchMove(e) {
if (!isDragging) return;
moveX = e.touches[0].clientX - startX;
moveY = e.touches[0].clientY - startY;
// Apply transform to the card
card.style.transform = `translate(${moveX}px, ${moveY}px) rotate(${moveX * 0.1}deg)`;
// Show appropriate hint based on direction
updateHints(moveX, moveY);
}
function handleTouchEnd(e) {
if (!isDragging) return;
// Calculate touch duration
const touchEndTime = new Date().getTime();
const touchDuration = touchEndTime - touchStartTime;
// Determine if this was a tap (short touch with minimal movement)
const absX = Math.abs(moveX || 0);
const absY = Math.abs(moveY || 0);
const isTap = touchDuration < 300 && Math.max(absX, absY) < 10;
isDragging = false;
if (isTap) {
// This was a tap, not a swipe
resetCardPosition();
handleCardClick(e);
} else if (Math.max(absX, absY) > swipeThreshold) {
// This was a swipe
if (absX > absY) {
// Horizontal swipe
if (moveX > 0) {
performSwipe('right');
} else {
performSwipe('left');
}
} else {
// Vertical swipe
if (moveY > 0) {
performSwipe('down');
} else {
performSwipe('up');
}
}
} else {
// Reset card position if swipe wasn't strong enough
resetCardPosition();
}
// Hide all hints
hideAllHints();
}
function handleMouseDown(e) {
// Store the initial position and set the dragging flag
startX = e.clientX;
startY = e.clientY;
isDragging = true;
card.classList.add('swiping');
// Prevent default to avoid text selection during drag
e.preventDefault();
}
function handleMouseMove(e) {
if (!isDragging) return;
moveX = e.clientX - startX;
moveY = e.clientY - startY;
// Apply transform to the card
card.style.transform = `translate(${moveX}px, ${moveY}px) rotate(${moveX * 0.1}deg)`;
// Show appropriate hint based on direction
updateHints(moveX, moveY);
}
function handleMouseUp(e) {
if (!isDragging) return;
// Determine if this was a click (minimal movement) or a swipe
const absX = Math.abs(moveX || 0);
const absY = Math.abs(moveY || 0);
isDragging = false;
if (Math.max(absX, absY) > swipeThreshold) {
if (absX > absY) {
// Horizontal swipe
if (moveX > 0) {
performSwipe('right');
} else {
performSwipe('left');
}
} else {
// Vertical swipe
if (moveY > 0) {
performSwipe('down');
} else {
performSwipe('up');
}
}
} else {
// Reset card position if swipe wasn't strong enough
resetCardPosition();
// We don't trigger click here because the card already has a click event listener
}
// Hide all hints
hideAllHints();
}
function updateHints(moveX, moveY) {
hideAllHints();
const absX = Math.abs(moveX);
const absY = Math.abs(moveY);
if (absX > absY) {
// Horizontal movement is dominant
if (moveX > 0) {
rightHint.style.opacity = '1';
} else {
leftHint.style.opacity = '1';
}
} else {
// Vertical movement is dominant
if (moveY > 0) {
downHint.style.opacity = '1';
} else {
upHint.style.opacity = '1';
}
}
}
function hideAllHints() {
leftHint.style.opacity = '0';
rightHint.style.opacity = '0';
upHint.style.opacity = '0';
downHint.style.opacity = '0';
}
function resetCardPosition() {
card.classList.remove('swiping');
card.style.transform = '';
}
function performSwipe(direction) {
// Add the appropriate swipe class
card.classList.add(`swipe-${direction}`);
// Update the last action text
lastActionText.textContent = `Last action: Swiped ${direction}`;
// Record the selection in the database if we have a current image
if (currentImageInfo) {
recordSelection(currentImageInfo, direction);
}
// After animation completes, reset and load a new image
setTimeout(() => {
card.classList.remove(`swipe-${direction}`);
card.classList.remove('swiping');
card.style.transform = '';
// Load a new random image from our server
loadNewImage();
}, 300);
}
// Function to record a selection in the database
function recordSelection(imageInfo, action) {
// Create the data to send
const data = {
path: imageInfo.path,
resolution: imageInfo.resolution,
action: action
};
// Send the data to the server
fetch('/record-selection', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => {
if (!response.ok) {
throw new Error('Failed to record selection');
}
return response.json();
})
.then(data => {
console.log('Selection recorded:', data);
})
.catch(error => {
console.error('Error recording selection:', error);
});
}
// Function to load a new image from our local server
function loadNewImage() {
// Show loading state
const img = card.querySelector('img');
img.style.opacity = '0.5';
// Fetch a random image from our API
fetch('/random-image')
.then(response => {
if (!response.ok) {
throw new Error('Failed to fetch image');
}
return response.json();
})
.then(data => {
// Store current image info
currentImageInfo = data;
// Extract filename from path
const pathParts = data.path.split('/');
const filename = pathParts[pathParts.length - 1];
currentImageInfo.filename = filename;
currentImageInfo.creation_date = data.creation_date || 'Unknown';
// Update the image source
img.onload = function() {
img.style.opacity = '1';
};
img.src = data.path;
// Update status with resolution info
const statusElement = document.querySelector('.status-area p:first-child');
statusElement.textContent = `Current resolution: ${data.resolution}`;
})
.catch(error => {
console.error('Error loading image:', error);
img.style.opacity = '1';
img.src = 'data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%22400%22%20height%3D%22400%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Crect%20width%3D%22400%22%20height%3D%22400%22%20fill%3D%22%23e0e0e0%22%2F%3E%3Ctext%20x%3D%22200%22%20y%3D%22200%22%20font-size%3D%2220%22%20text-anchor%3D%22middle%22%20alignment-baseline%3D%22middle%22%20fill%3D%22%23999%22%3EImage%20not%20found%3C%2Ftext%3E%3C%2Fsvg%3E';
});
}
// Function to handle card click for viewing full-resolution image
function handleCardClick(e) {
// Only process click if we have image info and we're not in the middle of a swipe
if (!currentImageInfo || card.classList.contains('swiping')) return;
// Prevent click from propagating (important for touch devices)
if (e) e.stopPropagation();
// Set the full-resolution image source
fullscreenImage.src = currentImageInfo.path;
// Update modal info
modalResolution.textContent = `Resolution: ${currentImageInfo.resolution}`;
modalFilename.textContent = `Filename: ${currentImageInfo.filename || 'Unknown'}`;
modalCreationDate.textContent = `Creation Date: ${currentImageInfo.creation_date || 'Unknown'}`;
// Display the modal
modal.style.display = 'block';
}
// Load initial image
loadNewImage();
});