Skip to content

Commit 12fa60e

Browse files
Btema2Jefino9488
authored andcommitted
fix(mobile): resolve touch scroll locking and double-swipe on mobile devices
1 parent 8240e1a commit 12fa60e

6 files changed

Lines changed: 35 additions & 19 deletions

File tree

‎src/app/globals.css‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
body {
2020
background-color: var(--color-axion-bg);
2121
color: var(--color-axion-text);
22-
overflow-x: hidden;
22+
overflow-x: clip;
2323
-webkit-font-smoothing: antialiased;
2424
-moz-osx-font-smoothing: grayscale;
2525
}

‎src/app/layout.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export default function RootLayout({
3636
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
3737
>
3838
<body>
39-
<div className="relative w-full overflow-x-hidden min-h-screen flex flex-col">
39+
<div className="relative w-full overflow-x-clip min-h-screen flex flex-col">
4040
<ParticleBackground />
4141
<Navbar />
4242
<SmoothScrolling>

‎src/app/page.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const ResearchShowcase = dynamic(() => import("@/components/ResearchShowcase"));
99
export default function Home() {
1010

1111
return (
12-
<main className="block w-full min-h-screen overflow-x-hidden relative">
12+
<main className="block w-full min-h-screen overflow-x-clip relative">
1313

1414
<div className="hidden md:block">
1515
<HeroScene />

‎src/components/FeatureShowcase.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ function FeatureRow({ group, index }: { group: typeof featureGroups[0]; index: n
235235
</div>
236236

237237
{/* Screens Block */}
238-
<div ref={screensRef} className={`flex-1 w-full md:w-auto relative z-10 flex items-center md:items-end ${group.screens.length === 1 ? "justify-center" : "justify-start"} md:justify-center gap-6 overflow-x-auto md:overflow-visible snap-x snap-mandatory md:snap-none [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none] pb-8 md:pb-0 pt-6 md:pt-0 px-6 md:px-0 -mx-6 md:mx-0`}>
238+
<div ref={screensRef} className={`flex-1 w-full md:w-auto relative z-10 flex items-center md:items-end ${group.screens.length === 1 ? "justify-center" : "justify-start"} md:justify-center gap-6 overflow-x-auto md:overflow-visible snap-x snap-mandatory md:snap-none [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none] pb-8 md:pb-0 pt-6 md:pt-0 px-6 md:px-0 -mx-6 md:mx-0 touch-pan-x touch-pan-y`}>
239239
{group.screens.map((screen, i) => {
240240
// Stagger vertical offset for visual interest
241241
const yOffset = i % 2 === 0 ? 0 : 24;
@@ -278,7 +278,7 @@ export default function FeatureShowcase() {
278278
}, []);
279279

280280
return (
281-
<section ref={sectionRef} className="relative w-full py-32 md:py-48 px-6 overflow-hidden">
281+
<section ref={sectionRef} className="relative w-full py-32 md:py-48 px-6 overflow-x-clip">
282282
{/* Section Header */}
283283
<div ref={headerRef} className="max-w-5xl mx-auto mb-10 text-center">
284284
<p className="text-sm uppercase tracking-[0.3em] text-[var(--color-axion-accent)] font-medium mb-4">Exclusive Features</p>

‎src/components/HeroSceneMobile.tsx‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export default function HeroSceneMobile() {
5959
}, []);
6060

6161
return (
62-
<div ref={containerRef} className="w-full bg-[var(--color-axion-bg)] text-white overflow-hidden">
62+
<div ref={containerRef} className="w-full bg-[var(--color-axion-bg)] text-white overflow-x-clip">
6363
{/* 1. Hero Entry */}
6464
<section className="relative min-h-[90svh] w-full flex flex-col items-center justify-between pt-[15vh] px-6">
6565
<div className="absolute top-1/4 left-1/2 -translate-x-1/2 w-[150vw] h-[150vw] bg-[var(--color-axion-accent)]/10 blur-[120px] rounded-full pointer-events-none" />
@@ -95,7 +95,7 @@ export default function HeroSceneMobile() {
9595
</div>
9696

9797
{/* Horizontal scroll of clocks on phones */}
98-
<div className="w-full overflow-x-auto pb-8 snap-x snap-mandatory flex gap-6 px-6 [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none] fade-up relative z-20">
98+
<div className="w-full overflow-x-auto pb-8 snap-x snap-mandatory flex gap-6 px-6 [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none] fade-up relative z-20 touch-pan-x touch-pan-y" data-lenis-prevent>
9999
{clockStyles.map((clock, i) => (
100100
<div key={i} className="snap-center shrink-0 flex flex-col items-center gap-6">
101101
<div className="relative w-[240px] sm:w-[260px] aspect-[9/20.5] rounded-[2rem] border-[3px] border-[#1a1a1a] overflow-hidden bg-black">
@@ -125,7 +125,7 @@ export default function HeroSceneMobile() {
125125
</div>
126126

127127
{/* Horizontal scroll of depth wallpapers */}
128-
<div className="w-full overflow-x-auto pb-8 snap-x snap-mandatory flex gap-6 px-6 [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none] fade-up relative z-20">
128+
<div className="w-full overflow-x-auto pb-8 snap-x snap-mandatory flex gap-6 px-6 [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none] fade-up relative z-20 touch-pan-x touch-pan-y" data-lenis-prevent>
129129
{depthWallpapers.map((wp, i) => (
130130
<div key={i} className="snap-center shrink-0 relative w-[240px] sm:w-[260px] aspect-[9/20.5] rounded-[2rem] border-[3px] border-white/10 overflow-hidden bg-black">
131131
<Image src={wp.src} alt={wp.title} fill sizes="(max-width: 768px) 100vw, 50vw" className="object-cover object-top" />

‎src/components/SmoothScrolling.tsx‎

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"use client";
22

33
import { ReactLenis } from "lenis/react";
4-
import { ReactNode, useEffect, useRef } from "react";
4+
import { ReactNode, useEffect, useRef, useState } from "react";
55
import gsap from "gsap";
66
import { ScrollTrigger } from "gsap/ScrollTrigger";
77

@@ -13,30 +13,46 @@ interface SmoothScrollingProps {
1313

1414
export default function SmoothScrolling({ children }: SmoothScrollingProps) {
1515
const lenisRef = useRef<any>(null);
16+
const [isTouch, setIsTouch] = useState<boolean>(false);
1617

1718
useEffect(() => {
19+
const isTouchDevice =
20+
"ontouchstart" in window ||
21+
navigator.maxTouchPoints > 0 ||
22+
window.matchMedia("(pointer: coarse)").matches;
23+
24+
setIsTouch(isTouchDevice);
25+
1826
gsap.config({ force3D: true });
1927
ScrollTrigger.config({
2028
limitCallbacks: true,
2129
ignoreMobileResize: true
2230
});
2331

24-
// Sync Lenis scroll with GSAP ticker to prevent jitter/bouncing
25-
function update(time: number) {
26-
lenisRef.current?.lenis?.raf(time * 1000);
32+
if (!isTouchDevice) {
33+
function update(time: number) {
34+
if (lenisRef.current?.lenis) {
35+
lenisRef.current.lenis.raf(time * 1000);
36+
}
37+
}
38+
39+
gsap.ticker.add(update);
40+
gsap.ticker.lagSmoothing(0); // Important for perfect sync
41+
42+
return () => {
43+
gsap.ticker.remove(update);
44+
};
2745
}
28-
29-
gsap.ticker.add(update);
30-
gsap.ticker.lagSmoothing(0); // Important for perfect sync
31-
32-
return () => {
33-
gsap.ticker.remove(update);
34-
};
3546
}, []);
3647

48+
if (isTouch) {
49+
return <>{children}</>;
50+
}
51+
3752
return (
3853
<ReactLenis root ref={lenisRef} autoRaf={false} options={{ lerp: 0.1, duration: 1.5, smoothWheel: true }}>
3954
{children}
4055
</ReactLenis>
4156
);
4257
}
58+

0 commit comments

Comments
 (0)