react-native-parallax-scroll-view
Advanced tools
Comparing version 0.16.20 to 0.17.0-rc
{ | ||
"name": "react-native-parallax-scroll-view", | ||
"version": "0.16.20", | ||
"version": "0.17.0-rc", | ||
"description": "A ScrollView-like component with parallax and sticky header support", | ||
@@ -13,3 +13,3 @@ "main": "src/index.js", | ||
"demo.ios.gif", | ||
"demo.android.gif", | ||
"demo.android.20160117.gif", | ||
"README.md", | ||
@@ -16,0 +16,0 @@ "LICENSE" |
@@ -24,3 +24,3 @@ # react-native-parallax-scroll-view | ||
| --- | ------- | | ||
| ![](./demo.ios.gif) | ![](./demo.android.gif) | | ||
| ![](./demo.ios.gif) | ![](./demo.android.20160117.gif) | | ||
@@ -36,3 +36,3 @@ ## Basic Usage | ||
<ParallaxScrollView | ||
headerBackgroundColor="blue" | ||
backgroundColor="blue" | ||
contentBackgroundColor="pink" | ||
@@ -71,7 +71,6 @@ parallaxHeaderHeight={300} | ||
| -------- | ---- | -------- | ----------- | | ||
| `renderParallaxHeader` | `func` | **Yes** |This renders the parallax header above the background. | | ||
| `parallaxHeaderHeight` | `number` | **Yes** |This is the height of parallax header. | | ||
| `headerBackgroundColor` | `string` | No | This is the background color of the sticky header, and also used as parallax header background color if `renderBackground` is not provided. (Defaults to `'#000'`) | | ||
| `contentBackgroundColor` | `string` | No | This is the background color of the content. (Defaults to `'#fff'`) | | ||
| `renderBackground` | `func` | No | This renders the background of the parallax header. Can be used to display cover images for example. (Defaults to an opaque background using `headerBackgroundColor`) | | ||
| `renderBackground` | `func` | No | This renders the background of the parallax header. Can be used to display cover images for example. (Defaults to an opaque background using `backgroundColor`) | | ||
| `backgroundSpeed` | `number` | No | The speed factor that the background moves at relative to the foreground. Defaults to 5. | | ||
| `renderForeground` | `func` | No |This renders the foreground header that moves at same speed as scroll content. | | ||
| `renderStickyHeader` | `func` | No | This renders an optional sticky header that will stick to the top of view when parallax header scrolls up. | | ||
@@ -81,2 +80,10 @@ | `stickyHeaderHeight` | `number` | If `renderStickyHeader` is used | If `renderStickyHeader` is set, then its height must be specified. | | ||
| `renderScrollComponent` | `func` | No | A function with input `props` and outputs a `ScrollView`-like component in which the content is rendered. This is useful if you want to provide your own scrollable component. (See: [https://github.com/exponentjs/react-native-scrollable-mixin](https://github.com/exponentjs/react-native-scrollable-mixin)) (By default, returns a `ScrollView` with the given props) | | ||
| `contentBackgroundColor` | `string` | No | This is the background color of the content. (Defaults to `'#fff'`) | | ||
| `onChangeHeaderVisibility` | `func` | No | A callback function that is invoked when the parallax header is hidden or shown (as the user is scrolling). Function is called with a `boolean` value to indicate whether header is visible or not. | | ||
## Changelog | ||
### 0.17.0 | ||
- Refactored parallax header calculations to keep their scroll speeds consistent with user scroll speed. | ||
- Parallax effect is now calculated using the `backgroundSpeed` prop. |
@@ -8,3 +8,3 @@ const React = require('react-native'); | ||
View | ||
} = React; | ||
} = React; | ||
@@ -19,11 +19,11 @@ const styles = require('./styles'); | ||
const midpoint = (a, b) => (a - b) / 2; | ||
const pivotPoint = (a, b) => (a - b); | ||
// Properties accepted by `ParallaxScrollView`. | ||
const IPropTypes = { | ||
headerBackgroundColor: string, | ||
backgroundColor: string, | ||
contentBackgroundColor: string, | ||
onChangeHeaderVisibility: func, | ||
parallaxHeaderHeight: number.isRequired, | ||
renderParallaxHeader: func.isRequired, | ||
renderForeground: func, | ||
renderStickyHeader: func, | ||
@@ -33,3 +33,4 @@ renderScrollComponent: func, | ||
renderBackground: func, | ||
stickyHeaderHeight: number | ||
stickyHeaderHeight: number, | ||
backgroundSpeed: number | ||
}; | ||
@@ -41,4 +42,7 @@ | ||
if (props.renderStickyHeader && !props.stickyHeaderHeight) { | ||
console.error('Property `stickyHeaderHeight` must be set if `renderStickyHeader` is used') | ||
console.warn('Property `stickyHeaderHeight` must be set if `renderStickyHeader` is used.'); | ||
} | ||
if (props.renderParallaxHeader && !props.renderForeground) { | ||
console.warn('Property `renderParallaxHeader` is deprecated. Use `renderForeground` instead.'); | ||
} | ||
this.state = { | ||
@@ -57,3 +61,5 @@ scrollY: new Animated.Value(0), | ||
contentBackgroundColor, | ||
headerBackgroundColor, | ||
backgroundSpeed, | ||
headerSpeed, | ||
backgroundColor, | ||
parallaxHeaderHeight, | ||
@@ -63,2 +69,3 @@ stickyHeaderHeight, | ||
renderFixedHeader, | ||
renderForeground, | ||
renderParallaxHeader, | ||
@@ -69,9 +76,9 @@ renderScrollComponent, | ||
...scrollViewProps | ||
} = this.props; | ||
} = this.props; | ||
const background = this._renderBackground({ headerBackgroundColor, parallaxHeaderHeight, stickyHeaderHeight, renderBackground }); | ||
const parallaxHeader = this._renderParallaxHeader({ parallaxHeaderHeight, stickyHeaderHeight, renderParallaxHeader }); | ||
const background = this._renderBackground({ backgroundSpeed, backgroundColor, parallaxHeaderHeight, stickyHeaderHeight, renderBackground }); | ||
const foreground = this._renderForeground({ headerSpeed, parallaxHeaderHeight, stickyHeaderHeight, renderForeground: renderForeground || renderParallaxHeader }); | ||
const bodyComponent = this._wrapChildren(children, { contentBackgroundColor, stickyHeaderHeight }); | ||
const footerSpacer = this._renderFooterSpacer({ contentBackgroundColor }); | ||
const maybeStickyHeader = this._maybeRenderStickyHeader({ parallaxHeaderHeight, stickyHeaderHeight, headerBackgroundColor, renderFixedHeader, renderStickyHeader }); | ||
const maybeStickyHeader = this._maybeRenderStickyHeader({ parallaxHeaderHeight, stickyHeaderHeight, backgroundColor, renderFixedHeader, renderStickyHeader }); | ||
const scrollElement = renderScrollComponent(scrollViewProps); | ||
@@ -90,3 +97,3 @@ | ||
}, | ||
parallaxHeader, | ||
foreground, | ||
bodyComponent, | ||
@@ -132,7 +139,7 @@ footerSpacer | ||
const mid = midpoint(parallaxHeaderHeight, stickyHeaderHeight); | ||
const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight); | ||
this._maybeUpdateScrollPosition(e); | ||
if (e.nativeEvent.contentOffset.y >= mid) { | ||
if (e.nativeEvent.contentOffset.y >= p) { | ||
onChangeHeaderVisibility(false); | ||
@@ -147,4 +154,3 @@ } else { | ||
// This optimizes the state update of current scrollY since we don't need to | ||
// perform any updates when user has scrolled past the midpoint of | ||
// parallaxHeaderHeight - stickyHeaderHeight. | ||
// perform any updates when user has scrolled past the pivot point. | ||
_maybeUpdateScrollPosition(e) { | ||
@@ -154,5 +160,5 @@ const { parallaxHeaderHeight, stickyHeaderHeight } = this.props; | ||
const { nativeEvent: { contentOffset: { y: offsetY } } } = e; | ||
const mid = midpoint(parallaxHeaderHeight, stickyHeaderHeight); | ||
const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight); | ||
if (offsetY <= mid || scrollY._value <= mid) { | ||
if (offsetY <= p || scrollY._value <= p) { | ||
scrollY.setValue(offsetY); | ||
@@ -173,9 +179,9 @@ } | ||
_renderBackground({ headerBackgroundColor, parallaxHeaderHeight, stickyHeaderHeight, renderBackground }) { | ||
_renderBackground({ backgroundSpeed, backgroundColor, parallaxHeaderHeight, stickyHeaderHeight, renderBackground }) { | ||
const { viewWidth, viewHeight, scrollY } = this.state; | ||
const mid = midpoint(parallaxHeaderHeight, stickyHeaderHeight); | ||
const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight); | ||
return ( | ||
<Animated.View | ||
style={[styles.backgroundImage, { | ||
backgroundColor: headerBackgroundColor, | ||
backgroundColor: backgroundColor, | ||
height: parallaxHeaderHeight, | ||
@@ -185,4 +191,4 @@ width: viewWidth, | ||
translateY: scrollY.interpolate({ | ||
inputRange: [0, mid], | ||
outputRange: [0, -mid], | ||
inputRange: [0, p], | ||
outputRange: [0, -(p / backgroundSpeed)], | ||
extrapolateRight: 'extend', | ||
@@ -206,4 +212,5 @@ extrapolateLeft: 'clamp' | ||
_renderParallaxHeader({ parallaxHeaderHeight, stickyHeaderHeight, renderParallaxHeader }) { | ||
const mid = midpoint(parallaxHeaderHeight, stickyHeaderHeight); | ||
_renderForeground({ parallaxHeaderHeight, stickyHeaderHeight, renderForeground }) { | ||
const { scrollY } = this.state; | ||
const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight); | ||
return ( | ||
@@ -213,14 +220,12 @@ <View style={styles.parallaxHeaderContainer}> | ||
style={[styles.parallaxHeader, { | ||
height: this.state.scrollY.interpolate({ | ||
inputRange: [0, parallaxHeaderHeight - stickyHeaderHeight], | ||
outputRange: [parallaxHeaderHeight, stickyHeaderHeight], | ||
extrapolate: 'clamp' | ||
}), | ||
opacity: this.state.scrollY.interpolate({ | ||
inputRange: [0, mid - 20, mid], | ||
outputRange: [1, .9, 0], | ||
height: parallaxHeaderHeight, | ||
opacity: scrollY.interpolate({ | ||
inputRange: [0, p * (2/3), p], | ||
outputRange: [1, 0.5, 0], | ||
extrapolate: 'extend' | ||
}) | ||
}]}> | ||
{ renderParallaxHeader() } | ||
<View style={{ height: parallaxHeaderHeight }}> | ||
{ renderForeground() } | ||
</View> | ||
</Animated.View> | ||
@@ -256,6 +261,6 @@ </View> | ||
_maybeRenderStickyHeader({ parallaxHeaderHeight, stickyHeaderHeight, headerBackgroundColor, renderFixedHeader, renderStickyHeader }) { | ||
_maybeRenderStickyHeader({ parallaxHeaderHeight, stickyHeaderHeight, backgroundColor, renderFixedHeader, renderStickyHeader }) { | ||
const { viewWidth, scrollY } = this.state; | ||
if (renderStickyHeader) { | ||
const mid = midpoint(parallaxHeaderHeight, stickyHeaderHeight); | ||
const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight); | ||
return ( | ||
@@ -265,6 +270,6 @@ <View style={[styles.stickyHeader, { width: viewWidth, height: stickyHeaderHeight }]}> | ||
style={{ | ||
backgroundColor: headerBackgroundColor, | ||
backgroundColor: backgroundColor, | ||
height: stickyHeaderHeight, | ||
opacity: scrollY.interpolate({ | ||
inputRange: [0, mid], | ||
inputRange: [0, p], | ||
outputRange: [0, 1], | ||
@@ -278,3 +283,3 @@ extrapolate: 'clamp' | ||
translateY: scrollY.interpolate({ | ||
inputRange: [0, mid], | ||
inputRange: [0, p], | ||
outputRange: [stickyHeaderHeight, 0], | ||
@@ -300,3 +305,4 @@ extrapolate: 'clamp' | ||
ParallaxScrollView.defaultProps = { | ||
headerBackgroundColor: '#000', | ||
backgroundSpeed: 5, | ||
backgroundColor: '#000', | ||
contentBackgroundColor: '#fff', | ||
@@ -307,2 +313,3 @@ onChangeHeaderVisibility: () => {}, | ||
renderParallaxHeader: () => <View/>, | ||
renderForeground: null, | ||
stickyHeaderHeight: 0 | ||
@@ -309,0 +316,0 @@ }; |
301
86
1733658