diff --git a/README.md b/README.md index e335bd2..1e71ba1 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ The `ParallaxScrollView` component adds a few additional properties, as describe | `renderStickyHeader` | `func` | No | This renders an optional sticky header that will stick to the top of view when parallax header scrolls up. | | `stickyHeaderHeight` | `number` | If `renderStickyHeader` is used | If `renderStickyHeader` is set, then its height must be specified. | | `contentContainerStyle` | `object` | No | These styles will be applied to the scroll view content container which wraps all of the child views. (same as for [ScrollView](https://facebook.github.io/react-native/docs/scrollview.html#contentcontainerstyle)) | +| `scrollingStickyHeader` | `bool` | No | This causes the sticky header to scroll into view. If set to false the sticky header will only fade in. | ## Latest changes diff --git a/src/index.js b/src/index.js index c43b031..36ef5bc 100644 --- a/src/index.js +++ b/src/index.js @@ -2,6 +2,7 @@ import React, { Component } from 'react'; import { Animated, Dimensions, + Platform, ScrollView, View, ViewPropTypes, @@ -45,6 +46,7 @@ const IPropTypes = { renderStickyHeader: func, stickyHeaderHeight: number, contentContainerStyle: ViewPropTypes.style, + scrollingStickyHeader: bool, }; class ParallaxScrollView extends Component { @@ -59,7 +61,7 @@ class ParallaxScrollView extends Component { this.state = { scrollY: new Animated.Value(0), viewHeight: window.height, - viewWidth: window.width + viewWidth: window.width, }; this._footerComponent = { setNativeProps() {} }; // Initial stub this._footerHeight = 0; @@ -83,6 +85,7 @@ class ParallaxScrollView extends Component { stickyHeaderHeight, style, contentContainerStyle, + scrollingStickyHeader, ...scrollViewProps } = this.props; @@ -90,12 +93,12 @@ class ParallaxScrollView extends Component { const foreground = this._renderForeground({ fadeOutForeground, parallaxHeaderHeight, stickyHeaderHeight, renderForeground: renderForeground || renderParallaxHeader }); const bodyComponent = this._wrapChildren(children, { contentBackgroundColor, stickyHeaderHeight, contentContainerStyle }); const footerSpacer = this._renderFooterSpacer({ contentBackgroundColor }); - const maybeStickyHeader = this._maybeRenderStickyHeader({ parallaxHeaderHeight, stickyHeaderHeight, backgroundColor, renderFixedHeader, renderStickyHeader }); + const maybeStickyHeader = this._maybeRenderStickyHeader({ parallaxHeaderHeight, stickyHeaderHeight, backgroundColor, renderFixedHeader, renderStickyHeader, scrollingStickyHeader }); const scrollElement = renderScrollComponent(scrollViewProps); return ( - this._maybeUpdateViewDimensions(e)}> + this._maybeUpdateViewDimensions(e)}> { background } { React.cloneElement(scrollElement, { @@ -110,7 +113,7 @@ class ParallaxScrollView extends Component { ) } { maybeStickyHeader } - + ); } @@ -142,8 +145,8 @@ class ParallaxScrollView extends Component { parallaxHeaderHeight, stickyHeaderHeight, onChangeHeaderVisibility, - onScroll: prevOnScroll = () => {} - } = this.props; + onScroll: prevOnScroll = () => {}, + } = this.props; const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight); @@ -177,7 +180,7 @@ class ParallaxScrollView extends Component { if (width !== this.state.viewWidth || height !== this.state.viewHeight) { this.setState({ viewWidth: width, - viewHeight: height + viewHeight: height, }); } } @@ -185,38 +188,42 @@ class ParallaxScrollView extends Component { _renderBackground({ fadeOutBackground, backgroundScrollSpeed, backgroundColor, parallaxHeaderHeight, stickyHeaderHeight, renderBackground }) { const { viewWidth, viewHeight, scrollY } = this.state; const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight); + const transform = [{ + translateY: interpolate(scrollY, { + inputRange: [0, p], + outputRange: [0, -(p / backgroundScrollSpeed)], + extrapolateRight: 'extend', + extrapolateLeft: 'clamp', + }), + }]; + if (Platform.OS === 'ios') { + transform.push({ + scale: interpolate(scrollY, { + inputRange: [-viewHeight, 0], + outputRange: [5, 1], + extrapolate: 'clamp', + }), + }); + } return ( - - + + { renderBackground() } - - + + ); } @@ -224,23 +231,23 @@ class ParallaxScrollView extends Component { const { scrollY } = this.state; const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight); return ( - - - - { renderForeground() } + + + + { renderForeground() } + + - - ); } @@ -252,29 +259,29 @@ class ParallaxScrollView extends Component { containerStyles.push(contentContainerStyle); return ( - { - // Adjust the bottom height so we can scroll the parallax header all the way up. - const { nativeEvent: { layout: { height } } } = e; - const footerHeight = Math.max(0, viewHeight - height - stickyHeaderHeight); - if (this._footerHeight !== footerHeight) { - this._footerComponent.setNativeProps({ style: { height: footerHeight }}); - this._footerHeight = footerHeight; - } - }}> + { + // Adjust the bottom height so we can scroll the parallax header all the way up. + const { nativeEvent: { layout: { height } } } = e; + const footerHeight = Math.max(0, viewHeight - height - stickyHeaderHeight); + if (this._footerHeight !== footerHeight) { + this._footerComponent.setNativeProps({ style: { height: footerHeight }}); + this._footerHeight = footerHeight; + } + }}> { children } - - ); + + ); } _renderFooterSpacer({ contentBackgroundColor }) { return ( - this._footerComponent = ref } style={{ backgroundColor: contentBackgroundColor }}/> + this._footerComponent = ref } style={{ backgroundColor: contentBackgroundColor }}/> ); } - _maybeRenderStickyHeader({ parallaxHeaderHeight, stickyHeaderHeight, backgroundColor, renderFixedHeader, renderStickyHeader }) { + _maybeRenderStickyHeader({ parallaxHeaderHeight, stickyHeaderHeight, backgroundColor, renderFixedHeader, renderStickyHeader, scrollingStickyHeader }) { const { viewWidth, scrollY } = this.state; if (renderStickyHeader || renderFixedHeader) { const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight); @@ -283,39 +290,42 @@ class ParallaxScrollView extends Component { outputRange: [0, stickyHeaderHeight], extrapolate: 'clamp', }); + const stickyHeader = scrollingStickyHeader ? ( + + {this.renderStickyHeader()} + + ) : renderStickyHeader(); return ( { renderStickyHeader ? ( - - - { renderStickyHeader() } - - + { stickyHeader } + ) : null } { renderFixedHeader && renderFixedHeader() } - + ); } else { return null; @@ -336,7 +346,8 @@ ParallaxScrollView.defaultProps = { renderParallaxHeader: renderEmpty, // Deprecated (will be removed in 0.18.0) renderForeground: null, stickyHeaderHeight: 0, - contentContainerStyle: null + contentContainerStyle: null, + scrollingStickyheader: true, }; module.exports = ParallaxScrollView;