Skip to content

Commit 4dd83b3

Browse files
committed
mobile support
1 parent 3efef52 commit 4dd83b3

File tree

1 file changed

+68
-1
lines changed

1 file changed

+68
-1
lines changed

apps/frontend/src/components/elements/Marquee.vue

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
@mouseenter="handleMouseEnter"
66
@mouseleave="handleMouseLeave"
77
@mousedown="handleMouseDown"
8+
@touchstart="handleTouchStart"
89
@click.capture="handleClick"
910
>
1011
<div
1112
ref="marqueeTrack"
1213
class="flex w-max"
1314
:style="{
1415
transform: `translateX(${translateX}px)`,
15-
cursor: isDragging ? 'grabbing' : 'grab'
16+
cursor: isDragging ? 'grabbing' : 'grab',
17+
touchAction: 'pan-y' // Allow vertical scrolling but prevent horizontal on touch
1618
}"
1719
>
1820
<!-- First set of items -->
@@ -154,6 +156,66 @@ const handleMouseUp = () => {
154156
document.removeEventListener('mouseup', handleMouseUp)
155157
}
156158
159+
// Touch event handlers for mobile
160+
const handleTouchStart = (e: TouchEvent) => {
161+
if (e.touches.length !== 1) return
162+
163+
isDragging.value = true
164+
const touch = e.touches[0]
165+
dragStartX.value = touch.clientX
166+
dragStartTranslateX.value = translateX.value
167+
hasDragged = false
168+
169+
// Reset velocity when starting a new drag
170+
velocity.value = 0
171+
172+
// Initialize velocity tracking
173+
lastMouseX = touch.clientX
174+
lastMouseTime = Date.now()
175+
176+
document.addEventListener('touchmove', handleTouchMove, { passive: false })
177+
document.addEventListener('touchend', handleTouchEnd)
178+
document.addEventListener('touchcancel', handleTouchEnd)
179+
}
180+
181+
const handleTouchMove = (e: TouchEvent) => {
182+
if (!isDragging.value || e.touches.length !== 1) return
183+
184+
const touch = e.touches[0]
185+
const deltaX = touch.clientX - dragStartX.value
186+
translateX.value = dragStartTranslateX.value + deltaX
187+
188+
// Track if user has actually dragged (moved more than 5px)
189+
if (Math.abs(deltaX) > 5) {
190+
hasDragged = true
191+
// Prevent scrolling when dragging
192+
e.preventDefault()
193+
}
194+
195+
// Calculate velocity for flick/momentum
196+
const currentTime = Date.now()
197+
const timeDelta = currentTime - lastMouseTime
198+
199+
if (timeDelta > 0) {
200+
const moveDelta = touch.clientX - lastMouseX
201+
velocity.value = moveDelta / timeDelta * 16 // normalize to ~60fps
202+
}
203+
204+
lastMouseX = touch.clientX
205+
lastMouseTime = currentTime
206+
207+
// Normalize position to keep within bounds for seamless looping
208+
normalizePosition()
209+
}
210+
211+
const handleTouchEnd = () => {
212+
isDragging.value = false
213+
normalizePosition()
214+
document.removeEventListener('touchmove', handleTouchMove)
215+
document.removeEventListener('touchend', handleTouchEnd)
216+
document.removeEventListener('touchcancel', handleTouchEnd)
217+
}
218+
157219
const animate = () => {
158220
if (!isDragging.value) {
159221
// Apply momentum if there's any velocity from flicking (even when hovering)
@@ -191,8 +253,13 @@ onUnmounted(() => {
191253
if (animationFrameId) {
192254
cancelAnimationFrame(animationFrameId)
193255
}
256+
// Clean up mouse event listeners
194257
document.removeEventListener('mousemove', handleMouseMove)
195258
document.removeEventListener('mouseup', handleMouseUp)
259+
// Clean up touch event listeners
260+
document.removeEventListener('touchmove', handleTouchMove)
261+
document.removeEventListener('touchend', handleTouchEnd)
262+
document.removeEventListener('touchcancel', handleTouchEnd)
196263
})
197264
</script>
198265

0 commit comments

Comments
 (0)