Skip to content

Instantly share code, notes, and snippets.

@clauderic
Last active October 20, 2022 03:50
Show Gist options
  • Select an option

  • Save clauderic/13cc9207a9e5db63ee67a1588eb11811 to your computer and use it in GitHub Desktop.

Select an option

Save clauderic/13cc9207a9e5db63ee67a1588eb11811 to your computer and use it in GitHub Desktop.
Smooth scrolling to a given offset in React Virtualized
/**
* Smoothly animate between two values
* @param {Number} fromValue - the initial value
* @param {Function} onUpdate - A function that is called on each tick
* @param {Function} onComplete - A callback that is fired once the scroll animation ends
* @param {Number} duration - the desired duration of the scroll animation
*/
export default function animate({
fromValue,
toValue,
onUpdate,
onComplete,
duration = 600,
}) {
const startTime = performance.now();
const tick = () => {
const elapsed = performance.now() - startTime;
window.requestAnimationFrame(() => onUpdate(
getValue(fromValue, toValue, elapsed, duration),
// Callback
elapsed <= duration
? tick
: onComplete
));
};
tick();
};
/**
* Given a start/end point of a scroll and time elapsed, calculate the scroll position we should be at
* @param {Number} start - the initial value
* @param {Number} stop - the final desired value
* @param {Number} elapsed - the amount of time elapsed since we started animating
* @param {Number} - duration - the duration of the animation
* @return {Number} - The value we should use on the next tick
*/
function getValue(start, end, elapsed, duration) {
if (elapsed > duration) return end;
return start + (end - start) * easing(elapsed / duration);
};
// For this example, I've used easeOutQuart, see https://gist.github.com/gre/1650294 for different easings
function easing(time) {
return 1 - (--time) * time * time * time;
};
import React, {Component} from 'react';
import {List} from 'react-virtualized';
import animate from './animate';
class App extends Component {
state = {
scrollTop: 0,
}
handleScroll = ({scrollTop}) => {
// Store the current scroll offset
this.scrollTop = scrollTop;
}
scrollTo(offset) {
animate({
fromValue: this.scrollTop,
toValue: offset,
onUpdate: (scrollTop, callback) => this.setState({scrollTop}, callback),
onComplete: () => console.log('Scroll animation complete!'),
});
}
rowRenderer = ({index, key, style}) => {
return (
<div key={key} style={style}>
Row #${index}
</div>
);
}
render() {
const {scrollTop} = this.state;
return (
<List
width={300}
height={300}
rowCount={500}
rowHeight={20}
rowRenderer={this.rowRenderer}
scrollTop={scrollTop}
onScroll={this.handleScroll}
/>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment