drei icon indicating copy to clipboard operation
drei copied to clipboard

Safari Scrolling List of Views Jittery

Open rossrossp opened this issue 3 months ago • 4 comments

  • three version: 0.162.0
  • @react-three/fiber version: 8.15.19
  • @react-three/drei version: 9.102.3
  • node version: v20.11.1
  • npm (or yarn) version: 10.2.4

Problem description:

Scrolling a list of Views and letting go so that the inertia continues, on Safari (iPhone or macOS), results in jittering of the Views, and a slight difference in offset between the View elements and the normal UI elements.

https://github.com/pmndrs/drei/assets/6325131/cea12a4b-ad0b-44df-b719-46574797097f

Relevant code:

import React from 'react'
import { Link } from 'react-router-dom';
import { Canvas, useFrame, ThreeElements } from '@react-three/fiber'
import { View, PerspectiveCamera } from '@react-three/drei';

import { motion } from 'framer-motion';

const ScrollingList: React.FC = () => {

  return (
    <motion.div initial={{opacity: 0}} animate={{opacity: 1, transition: {duration: 1 }}} exit={{opacity: 0, transition: {duration: 1 }}} className="w-screen h-screen flex flex-col overflow-y-scroll overflow-x-hidden bg-white">
      <h1 className="text-2xl font-black text-yellow-500 mt-12 ml-8">My Boxes</h1>
      <div className="flex-grow">
        {Array.apply(null, Array(20)).map(function(x, i) {
          return (
            <div className="h-48" key={"a"+i}>
              <div className="flex flex-row pb-10 justify-center">
                  {Array.apply(null, Array(3)).map(function(xx, ii) {
                    return (
                      <div className="flex flex-col" key={"b"+ii}>
                        <div className="w-32 h-36 overflow-hidden">
                          <View className="w-full h-full inline-block overflow-hidden">
                            <PerspectiveCamera makeDefault fov={40} position={[0, 5, 20]} />
                            <mesh position={[0, 0, 0]} scale={[3, 3, 3]}>
                              <boxGeometry />
                              <meshStandardMaterial color="#adadad" />
                            </mesh>
                          </View>
                        </div>
                        <div className="text-center">
                          <Link to={"/bla/"+i+"-"+ii} className="m-auto">
                            <h1 className="text-xl font-black text-yellow-500">Box</h1>
                            <h1 className="text-sm font-black text-gray-500">500</h1>
                          </Link>
                        </div>
                      </div>
                    )
                  })}
              </div>
            </div>
          )
        })}
        <Canvas
          style={{ position: 'fixed', top: 0, bottom: 0, left: 0, right: 0, overflow: 'hidden' }}
          eventSource={document.getElementById('root')!}>
          <View.Port />
        </Canvas>
      </div>
    </motion.div>
  );
};

export default ScrollingList;

Suggested solution:

The effect isn't as pronounced in Chrome and Firefox, so perhaps analysis could start with the difference between them. Edit: Chrome on an iPhone has some jitter, where chrome in macOS doesn't.

rossrossp avatar Mar 22 '24 21:03 rossrossp

Just found this as well. ScrollControls is super noticeable.

benhylak avatar Mar 23 '24 01:03 benhylak

For easy replication, try this in Safari: https://codesandbox.io/p/sandbox/useintersect-and-scrollcontrols-gsm1y?file=%2Fsrc%2Findex.js

rossrossp avatar Mar 24 '24 11:03 rossrossp

fwiw on ScrollControls I'm only seeing this jitter on iOS

benhylak avatar Mar 24 '24 19:03 benhylak

Just discovered this as well. I was getting jitter in Safari and inertia in Chrome. After doing some digging and looking at a few examples I noticed something. Have a look at the following Code Sandbox:

https://codesandbox.io/s/bp6tmc

Notice the App.js uses a smooth scrolling package called @studio-freight/lenis, and it calls addEffect from @react-three/fiber to implement the Lenis instance. Also notice that the Lenis instance has { syncTouch: true } set.

The example has no jitter and no inertia, but as soon as you comment the two lines out with the Lenis instance and the addEffect, the jitter/inertia then become visible. I'm not sure why Lenis makes the jitter go away, but it's a fix for anyone looking.

I managed to fix the jitter and inertia in my own application by using ReactLenis with the useLenis hook instead of regular lenis. Hopefully this gets you further!

mrcleandean avatar May 17 '24 00:05 mrcleandean