mirror of
https://github.com/zhigang1992/react-native.git
synced 2026-05-12 11:40:33 +08:00
Implement sticky headers in JS using Native Animated
Summary: This re-implements sticky headers in JS to make it work on Android. The only change that was needed was to expose a way to attach a an animated value to an event manually since we can't use the Animated wrapper and `Animated.event` to do it for us because this is implemented directly in the `ScrollView` component. Simply exposed `attachNativeEvent` that takes a ref, event name and event object mapping. This is what is used by `Animated.event`. TODO: - Need to check why momentum scrolling isn't triggering scroll events properly on Android. - Remove native iOS implementation - cleanup / fix flow **Test plan** Test the example list in UIExplorer, test the ListViewPaging example. Closes https://github.com/facebook/react-native/pull/11315 Differential Revision: D4450278 Pulled By: sahrens fbshipit-source-id: fec8da2cffce9807d74f8e518ebdefeb6a708667
This commit is contained in:
committed by
Facebook Github Bot
parent
da04a6b1f3
commit
77b8c09727
101
Libraries/Components/ScrollView/ScrollViewStickyHeader.js
Normal file
101
Libraries/Components/ScrollView/ScrollViewStickyHeader.js
Normal file
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule ScrollViewStickyHeader
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const Animated = require('Animated');
|
||||
const React = require('React');
|
||||
const StyleSheet = require('StyleSheet');
|
||||
|
||||
type Props = {
|
||||
children?: React.Element<*>,
|
||||
scrollAnimatedValue: Animated.Value,
|
||||
onLayout: (event: Object) => void,
|
||||
};
|
||||
|
||||
class ScrollViewStickyHeader extends React.Component {
|
||||
props: Props;
|
||||
state = {
|
||||
measured: false,
|
||||
layoutY: 0,
|
||||
nextHeaderLayoutY: (null: ?number),
|
||||
};
|
||||
|
||||
setNextHeaderY(y: number) {
|
||||
this.setState({ nextHeaderLayoutY: y });
|
||||
}
|
||||
|
||||
_onLayout = (event) => {
|
||||
this.setState({
|
||||
measured: true,
|
||||
layoutY: event.nativeEvent.layout.y,
|
||||
});
|
||||
|
||||
this.props.onLayout(event);
|
||||
};
|
||||
|
||||
render() {
|
||||
const {measured, layoutY, nextHeaderLayoutY} = this.state;
|
||||
|
||||
let translateY;
|
||||
if (measured) {
|
||||
// The interpolation looks like:
|
||||
// - Negative scroll: no translation
|
||||
// - From 0 to the y of the header: no translation. This will cause the header
|
||||
// to scroll normally until it reaches the top of the scroll view.
|
||||
// - From the header y to the next header y: translate equally to scroll.
|
||||
// This will cause the header to stay at the top of the scroll view.
|
||||
// - Past the the next header y: no more translation. This will cause the header
|
||||
// to continue scrolling up and make room for the next sticky header.
|
||||
// In the case that there is no next header just translate equally to
|
||||
// scroll indefinetly.
|
||||
const inputRange = [-1, 0, layoutY];
|
||||
const outputRange: Array<number> = [0, 0, 0];
|
||||
if (nextHeaderLayoutY != null) {
|
||||
inputRange.push(nextHeaderLayoutY, nextHeaderLayoutY + 1);
|
||||
outputRange.push(nextHeaderLayoutY - layoutY, nextHeaderLayoutY - layoutY);
|
||||
} else {
|
||||
inputRange.push(layoutY + 1);
|
||||
outputRange.push(1);
|
||||
}
|
||||
translateY = this.props.scrollAnimatedValue.interpolate({
|
||||
inputRange,
|
||||
outputRange,
|
||||
});
|
||||
} else {
|
||||
translateY = 0;
|
||||
}
|
||||
|
||||
const child = React.Children.only(this.props.children);
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
collapsable={false}
|
||||
onLayout={this._onLayout}
|
||||
style={[child.props.style, styles.header, {transform: [{translateY}]}]}>
|
||||
{React.cloneElement(child, {
|
||||
style: styles.fill,
|
||||
})}
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
header: {
|
||||
zIndex: 10,
|
||||
},
|
||||
fill: {
|
||||
flex: 1,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = ScrollViewStickyHeader;
|
||||
Reference in New Issue
Block a user