Initial commit: Image Swipe App with SQLite database
This commit is contained in:
344
script.js
Normal file
344
script.js
Normal file
@@ -0,0 +1,344 @@
|
||||
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();
|
||||
});
|
||||
Reference in New Issue
Block a user