Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Animating .scrollTo() / .scrollToIndex() ❓ #16

Closed
stnwk opened this issue Jul 20, 2018 · 13 comments
Closed

Animating .scrollTo() / .scrollToIndex() ❓ #16

stnwk opened this issue Jul 20, 2018 · 13 comments
Labels
💬 question Further information is requested

Comments

@stnwk
Copy link

stnwk commented Jul 20, 2018

Hi there :)

Thanks for this great lib!

I'm experimenting with it and I wondered whether it's somehow possible to animate the scrolling behavior ("smooth scroll") when using scrollTo() / scrollToIndex()?

@bvaughn bvaughn added the 💬 question Further information is requested label Jul 20, 2018
@bvaughn
Copy link
Owner

bvaughn commented Jul 20, 2018

Thanks!

Yup, you could do something like this: https://codesandbox.io/s/k2lpl9m0l3

@bvaughn bvaughn closed this as completed Jul 20, 2018
@stnwk
Copy link
Author

stnwk commented Jul 20, 2018

Thank you, that helps!

@EyMaddis
Copy link

This would be better, but does not work for all browsers (but there is a polyfill):

...
import { findDOMNode } from 'react-dom'

class MyScroller extends React.Component {
   list = React.createRef()
 scrollToAnimated(index) {
  const el = findDOMNode(this.list.current)
  if (el) {
    el.scrollTo({
     behavior: 'smooth',
     left: index * itemWidth
    })
  }
 }

 render() {
   return <List ref={this.list} ... />
 }
}

It would be great if there was a proper way to access this through react-window or get the calculated offsets for an index, @bvaughn.

@cquiroz
Copy link

cquiroz commented Jun 15, 2019

The sandbox of this example seems to be deleted, Do you have another?

@bvaughn
Copy link
Owner

bvaughn commented Jun 15, 2019

Sandbox is still there.

@cquiroz
Copy link

cquiroz commented Jun 15, 2019

Yeah sorry, somehow it showed as deleted on FF but it is fine on Chrome, perhaps on of my addons

@gauthamchandra
Copy link

This is easier to do with the outerRef prop which gives you a reference to the scrollable div.

You could then use that in combination with the standard Element.scrollTo() API.

So it would something along the lines of this:

export default VirtualizedList extends React.Component {
   constructor() {
     this.listRef = React.createRef();
     this.scrollableContainerRef = React.createRef();
   }

  scrollTo(scrollOffset) {
    // note that my list is vertical which is why I am feeding this to the "top" prop.
    this.scrollableContainerRef.current.scrollTo({ 
      left: 0, 
      top: scrollOffset,
      behavior: 'smooth',
    });
  }

  render() {
    <List 
      ref={this.listRef}
      outerRef={this.scrollableContainerRef}
      layout="vertical"
      ...
  }
}

NOTE: I removed all the extraneous code and props above to keep things brief.

@UCAudio
Copy link

UCAudio commented Oct 31, 2020

scrollTo

How would you do this within a functional component? I already have from and to position variables, and I'm trying to make the List scroll from PositionA (current scrollTop) to PositionB (a position I have defined). I'm having trouble getting the scrollTo(scrollOffset) { to work as the syntax shows an error when used in my functional component. I would like to trigger the scrolling behaving when a certain piece of state changes.

@EyMaddis
Copy link

EyMaddis commented Nov 2, 2020

untested:

function MyComp() {
    const listOuterRef = useRef(null)

    const scrollTo = useCallback((scrollOffset) => {
        listOuterRef.current?.scrollTo({ 
          left: 0, 
          top: scrollOffset,
          behavior: 'smooth',
        });
    }, [])


    return <List outerRef={listOuterRef} ... />
}

if you want to be fancy:

function useSmoothScroll() {
    const listOuterRef = useRef(null)

    const scrollTo = useCallback((scrollOffset) => {
        listOuterRef.current?.scrollTo({ 
          left: 0, 
          top: scrollOffset,
          behavior: 'smooth',
        });
    }, [])
    return {
        scrollableRef: listOuterRef,   
        scrollTo
     }
}
function MyComp() {
    const { scrollableRef, scrollTo } =  useSmoothScroll()
    // use scrollTo() in a useEffect or onClick
    return <List outerRef={scrollableRef} ... />
}

@UCAudio
Copy link

UCAudio commented Nov 5, 2020

untested:

function MyComp() {
    const listOuterRef = useRef(null)

    const scrollTo = useCallback((scrollOffset) => {
        listOuterRef.current?.scrollTo({ 
          left: 0, 
          top: scrollOffset,
          behavior: 'smooth',
        });
    }, [])


    return <List outerRef={listOuterRef} ... />
}

if you want to be fancy:

function useSmoothScroll() {
    const listOuterRef = useRef(null)

    const scrollTo = useCallback((scrollOffset) => {
        listOuterRef.current?.scrollTo({ 
          left: 0, 
          top: scrollOffset,
          behavior: 'smooth',
        });
    }, [])
    return {
        scrollableRef: listOuterRef,   
        scrollTo
     }
}
function MyComp() {
    const { scrollableRef, scrollTo } =  useSmoothScroll()
    // use scrollTo() in a useEffect or onClick
    return <List outerRef={scrollableRef} ... />
}

Thank you so much for the reply on this. I've spent countless hours researching a possible solution and this helped me a lot. I got the scrolling working thanks to your help! Unfortunately my implementation is very laggy with react-window so I had to switch back to react-virtualized List and it performs better for what I'm doing. However, there is no outerRef in react-virtualized, and I cannot figure out any possible way to set a ref for the scrollable div there, so I'm unable to animate scrolling this way with react-virtualized.

Do you have any idea how something like this is possible with react-virtualized using the List?

@Daniel-Alonso-Exabeam
Copy link

Daniel-Alonso-Exabeam commented Feb 18, 2021

jquery provides an easy scroll animation function with many options to fine tune the behavior.
You can convert the outerRef into a jquery element and then call the animate function on it.
https://api.jquery.com/animate/

Pseudocode:

const listOuterRef = useRef();

useEffect(() => {
 $(listOuterRef.current).animate(
    {
      scrollTop: 100,
    },
    {
      duration: 500,
      // other options can be passed to customize animation behavior such as easing, callbacks, etc
    },
  );
});

return <List outerRef={listOuterRef}>...</List>;

jquery seems to be less used in the React world, but it is fantastic for any scenario where you need to read/write the DOM directly. Should definitely be used sparingly though. Since it can easily undermine the whole point of using React in the first place.

@petrsuhin-al
Copy link

Just add to your global or component styles:

.List {scroll-behavior: smooth;}

IMHO, the simplest and most elegant solution, I don't understand why no one suggested it

@mmarzullotv
Copy link

Just add to your global or component styles:

.List {scroll-behavior: smooth;}

IMHO, the simplest and most elegant solution, I don't understand why no one suggested it

This workaround isn't actually supported properly by Safari (macOS / iOS). We might need to add a polyfill.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💬 question Further information is requested
Projects
None yet
Development

No branches or pull requests

9 participants