Complete 3.3.2: Touch Interaction Optimization
- Verified 44px minimum touch targets already in place - Added swipe gesture support for mobile chat toggling - Implemented pull-to-refresh functionality for mobile - Added double-tap gesture for fullscreen toggle on video area - All touch interactions are mobile-only to avoid desktop conflicts
This commit is contained in:
parent
68b97e946b
commit
a7fa21d3fa
2 changed files with 179 additions and 3 deletions
|
|
@ -346,9 +346,11 @@ Always come back and update UI_UPDATE.MD once complete with task and task item.
|
|||
**Notes:** Implemented comprehensive mobile-first responsive design. Updated layout.css to use BEM class names, added vertical stacking layout for mobile (flex-direction: column), chat slides up from bottom as modal overlay on mobile instead of collapsing to the side. Enhanced touch-friendly navigation with 44px minimum touch targets already in place. Updated ui-controls.js to handle mobile chat overlay behavior. Desktop remains side-by-side layout while mobile prioritizes video content with chat on-demand.
|
||||
|
||||
#### 3.3.2: Touch Interaction Optimization
|
||||
- [ ] Ensure 44px minimum touch targets
|
||||
- [ ] Implement swipe gesture support
|
||||
- [ ] Add mobile-specific interactive elements
|
||||
- [x] Ensure 44px minimum touch targets
|
||||
- [x] Implement swipe gesture support
|
||||
- [x] Add mobile-specific interactive elements
|
||||
|
||||
**Notes:** Implemented comprehensive touch interaction enhancements for mobile devices. The 44px minimum touch targets were already in place using var(--min-tap-target-size, 44px) in components.css. Added swipe gesture support on video area for chat toggling (swipe left to hide, right to show chat on mobile). Implemented pull-to-refresh functionality that triggers refresh when pulling down from top of page on mobile. Added double-tap gesture on video area to toggle fullscreen. All touch interactions are mobile-only to avoid conflicts on desktop.
|
||||
|
||||
#### 3.3.3: Mobile Navigation
|
||||
- [ ] Create bottom navigation pattern
|
||||
|
|
|
|||
|
|
@ -195,6 +195,163 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Touch gesture handling for mobile interactions
|
||||
let touchStartX = 0;
|
||||
let touchStartY = 0;
|
||||
let touchStartTime = 0;
|
||||
let isSwipeGesture = false;
|
||||
|
||||
function handleTouchStart(event) {
|
||||
touchStartX = event.touches[0].clientX;
|
||||
touchStartY = event.touches[0].clientY;
|
||||
touchStartTime = Date.now();
|
||||
isSwipeGesture = false;
|
||||
}
|
||||
|
||||
function handleTouchMove(event) {
|
||||
if (!event.touches || event.touches.length === 0) return;
|
||||
|
||||
const touchCurrentX = event.touches[0].clientX;
|
||||
const touchCurrentY = event.touches[0].clientY;
|
||||
const deltaX = touchCurrentX - touchStartX;
|
||||
const deltaY = touchCurrentY - touchStartY;
|
||||
|
||||
// Determine if this is a horizontal swipe (more horizontal than vertical movement)
|
||||
if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 50) {
|
||||
isSwipeGesture = true;
|
||||
}
|
||||
}
|
||||
|
||||
function handleTouchEnd(event) {
|
||||
if (!touchStartTime) return;
|
||||
|
||||
const touchEndTime = Date.now();
|
||||
const touchDuration = touchEndTime - touchStartTime;
|
||||
|
||||
if (touchDuration < 1000 && isSwipeGesture) { // Swipe gesture within 1 second
|
||||
const touchEndX = event.changedTouches[0].clientX;
|
||||
const deltaX = touchEndX - touchStartX;
|
||||
const minSwipeDistance = 75; // Minimum swipe distance in pixels
|
||||
|
||||
if (Math.abs(deltaX) > minSwipeDistance) {
|
||||
const isMobile = window.innerWidth <= AppConfig.ui.mobileBreakpoint;
|
||||
|
||||
if (isMobile) {
|
||||
// On mobile, swipe left to hide chat, right to show chat
|
||||
if (deltaX < 0) {
|
||||
// Swipe left - hide chat
|
||||
if (!AppState.chatCollapsed) {
|
||||
toggleChat();
|
||||
}
|
||||
} else {
|
||||
// Swipe right - show chat
|
||||
if (AppState.chatCollapsed) {
|
||||
toggleChat();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Reset touch tracking
|
||||
touchStartX = 0;
|
||||
touchStartY = 0;
|
||||
touchStartTime = 0;
|
||||
isSwipeGesture = false;
|
||||
}
|
||||
|
||||
// Pull-to-refresh functionality for mobile
|
||||
let pullStartY = 0;
|
||||
let pullDistance = 0;
|
||||
let isPulling = false;
|
||||
const pullThreshold = 80; // Minimum pull distance to trigger refresh
|
||||
|
||||
function handlePullToRefreshTouchStart(event) {
|
||||
if (window.innerWidth > AppConfig.ui.mobileBreakpoint) return; // Only on mobile
|
||||
|
||||
pullStartY = event.touches[0].clientY;
|
||||
pullDistance = 0;
|
||||
isPulling = true;
|
||||
}
|
||||
|
||||
function handlePullToRefreshTouchMove(event) {
|
||||
if (!isPulling || window.innerWidth > AppConfig.ui.mobileBreakpoint) return;
|
||||
|
||||
const currentY = event.touches[0].clientY;
|
||||
pullDistance = currentY - pullStartY;
|
||||
|
||||
// Only allow pull down from top of page
|
||||
if (window.scrollY === 0 && pullDistance > 0) {
|
||||
event.preventDefault(); // Prevent default scrolling behavior
|
||||
|
||||
const pullIndicator = document.getElementById('pullRefreshIndicator');
|
||||
if (pullIndicator) {
|
||||
const opacity = Math.min(pullDistance / pullThreshold, 1);
|
||||
pullIndicator.style.opacity = opacity;
|
||||
pullIndicator.style.transform = `translateY(${Math.min(pullDistance * 0.5, 40)}px)`;
|
||||
}
|
||||
} else {
|
||||
pullDistance = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function handlePullToRefreshTouchEnd() {
|
||||
if (!isPulling) return;
|
||||
|
||||
isPulling = false;
|
||||
|
||||
if (pullDistance > pullThreshold && window.scrollY === 0) {
|
||||
// Trigger refresh
|
||||
performPullToRefresh();
|
||||
}
|
||||
|
||||
// Reset pull indicator
|
||||
const pullIndicator = document.getElementById('pullRefreshIndicator');
|
||||
if (pullIndicator) {
|
||||
pullIndicator.style.opacity = '0';
|
||||
pullIndicator.style.transform = 'translateY(0px)';
|
||||
}
|
||||
|
||||
pullStartY = 0;
|
||||
pullDistance = 0;
|
||||
}
|
||||
|
||||
function performPullToRefresh() {
|
||||
showToast('Refreshing...', 0); // Show loading message
|
||||
|
||||
// Trigger the refresh by reloading data
|
||||
if (window.ChatSystem && typeof window.ChatSystem.refresh === 'function') {
|
||||
window.ChatSystem.refresh().then(() => {
|
||||
showToast('Content refreshed', 2000);
|
||||
}).catch(() => {
|
||||
showToast('Refresh failed', 2000);
|
||||
});
|
||||
} else {
|
||||
// Fallback: refresh the page after a short delay
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
|
||||
// Double-tap handler for video area (fullscreen toggle)
|
||||
let lastTapTime = 0;
|
||||
const doubleTapDelay = 300; // milliseconds
|
||||
|
||||
function handleVideoDoubleTap(event) {
|
||||
const currentTime = Date.now();
|
||||
const timeSinceLastTap = currentTime - lastTapTime;
|
||||
|
||||
if (timeSinceLastTap < doubleTapDelay && timeSinceLastTap > 0) {
|
||||
// Double tap detected - toggle fullscreen
|
||||
event.preventDefault();
|
||||
toggleFullscreen();
|
||||
lastTapTime = 0;
|
||||
} else {
|
||||
lastTapTime = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
// Page visibility API handler
|
||||
function handleVisibilityChange() {
|
||||
if (!document.hidden && !AppState.chatCollapsed) {
|
||||
|
|
@ -268,6 +425,23 @@
|
|||
// Page visibility for notification clearing
|
||||
DOMUtils.addEvent(document, 'visibilitychange', handleVisibilityChange);
|
||||
|
||||
// Touch gesture support for mobile
|
||||
const videoSection = document.getElementById('videoSection');
|
||||
if (videoSection) {
|
||||
// Swipe gestures on video area for chat toggle
|
||||
DOMUtils.addEvent(videoSection, 'touchstart', handleTouchStart, { passive: true });
|
||||
DOMUtils.addEvent(videoSection, 'touchmove', handleTouchMove, { passive: true });
|
||||
DOMUtils.addEvent(videoSection, 'touchend', handleTouchEnd, { passive: true });
|
||||
|
||||
// Double-tap on video for fullscreen
|
||||
DOMUtils.addEvent(videoSection, 'touchend', handleVideoDoubleTap);
|
||||
}
|
||||
|
||||
// Pull-to-refresh on the whole document (only on mobile)
|
||||
DOMUtils.addEvent(document, 'touchstart', handlePullToRefreshTouchStart, { passive: true });
|
||||
DOMUtils.addEvent(document, 'touchmove', handlePullToRefreshTouchMove, { passive: false });
|
||||
DOMUtils.addEvent(document, 'touchend', handlePullToRefreshTouchEnd, { passive: true });
|
||||
|
||||
AppLogger.log('UI controls event listeners initialized');
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue