react-native-parallax-scroll-view
Advanced tools
Comparing version 0.19.0 to 0.20.1
## Changelog | ||
### 0.20.1 | ||
- Added prop to change interpolated Scale Output Value | ||
### 0.20.0 // Rodrigocs | ||
- Now uses native driver, and tested with React Native 0.46.0 | ||
- Adds `useNativeDriver` to improve performance, but renderScrollComponent must be a Animated component ( ie: Animated.createAnimatedComponent(component)) | ||
### 0.19.0 | ||
- Fixes compatibility with React Native 0.27.2 | ||
- Adds `contentContainerStyle` prop to style scroll container (thanks @alaycock) | ||
- Adds `contentContainerStyle` prop to style scroll container (thanks [@alaycock](https://github.com/alaycock) | ||
@@ -12,3 +19,3 @@ ### 0.18.1 | ||
on `style` (#23) | ||
### 0.18.0 (Compatibility with React Native 0.20.0) | ||
@@ -15,0 +22,0 @@ |
{ | ||
"name": "react-native-parallax-scroll-view", | ||
"version": "0.19.0", | ||
"description": "A ScrollView-like component with parallax and sticky header support", | ||
"main": "src/index.js", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/jaysoo/react-native-parallax-scroll-view" | ||
}, | ||
"files": [ | ||
"src", | ||
"demo.ios.gif", | ||
"demo.android.20160117.gif", | ||
"README.md", | ||
"LICENSE" | ||
], | ||
"keywords": [ | ||
"react-native", | ||
"react", | ||
"parallax", | ||
"scrollable", | ||
"scrollview", | ||
"sticky", | ||
"react-component", | ||
"ios", | ||
"android" | ||
], | ||
"author": "Jack Hsu", | ||
"license": "ISC" | ||
"name": "react-native-parallax-scroll-view", | ||
"version": "0.20.1", | ||
"description": "A ScrollView-like component with parallax and sticky header support", | ||
"main": "src/index.js", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/jaysoo/react-native-parallax-scroll-view" | ||
}, | ||
"files": [ | ||
"src", | ||
"demo.ios.gif", | ||
"demo.android.20160117.gif", | ||
"README.md", | ||
"LICENSE" | ||
], | ||
"keywords": [ | ||
"react-native", | ||
"react", | ||
"parallax", | ||
"scrollable", | ||
"scrollview", | ||
"sticky", | ||
"react-component", | ||
"ios", | ||
"android" | ||
], | ||
"author": "Jack Hsu", | ||
"license": "ISC", | ||
"dependencies": { | ||
"prop-types": "^15.6.0" | ||
} | ||
} |
[![](https://img.shields.io/npm/dm/react-native-parallax-scroll-view.svg?style=flat-square)](https://www.npmjs.com/package/react-native-parallax-scroll-view) | ||
# Rodrigocs - Animated Driver | ||
This component now uses Native Driver by default. | ||
Remember to pass a Animated component to `renderScrollComponent`, by default it has `Animated.ScrollView` | ||
# Example | ||
```js | ||
import ParallaxScrollView from 'react-native-parallax-scroll-view'; | ||
import CustomScrollView from 'custom-scroll-view' | ||
const AnimatedCustomScrollView = Animated.createAnimatedComponent(CustomScrollView) | ||
render() { | ||
return ( | ||
<ParallaxScrollView | ||
backgroundColor="blue" | ||
contentBackgroundColor="pink" | ||
parallaxHeaderHeight={300} | ||
// renderScrollComponent={() => <Animated.View />} | ||
renderScrollComponent={() => <AnimatedCustomScrollView />} | ||
renderForeground={() => ( | ||
<View style={{ height: 300, flex: 1, alignItems: 'center', justifyContent: 'center' }}> | ||
<Text>Hello World!</Text> | ||
</View> | ||
)}> | ||
<View style={{ height: 500 }}> | ||
<Text>Scroll me</Text> | ||
</View> | ||
</ParallaxScrollView> | ||
); | ||
} | ||
``` | ||
# react-native-parallax-scroll-view | ||
@@ -80,25 +113,18 @@ | ||
| `renderForeground` | `func` | No |This renders the foreground header that moves at same speed as scroll content. | | ||
| `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) | | ||
| `renderScrollComponent` | `func` | No | A function with input `props` and outputs an `Animated.ScrollView`-like component in which the content is rendered. This is useful if you want to provide your own scrollable component, remember however to make it an Animated component. (See: [https://github.com/exponentjs/react-native-scrollable-mixin](https://github.com/exponentjs/react-native-scrollable-mixin)) (By default, returns a `Animated.ScrollView` with the given props) | | ||
| `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)) | | ||
| `outputScaleValue` | `number` | No | The value for the scale interpolation output value, default `5` | | ||
## Latest changes | ||
### 0.19.0 | ||
### 0.20.1 | ||
- Added prop to change interpolated Scale Output Value | ||
- Fixes compatibility with React Native 0.27.2 | ||
- Adds `contentContainerStyle` prop to style scroll container (thanks @alaycock) | ||
### 0.20.0 // Rodrigocs | ||
- Now uses native driver, and tested with React Native 0.46.0 | ||
- Adds `useNativeDriver` to improve performance, but renderScrollComponent must be a Animated component ( ie: Animated.createAnimatedComponent(component)) | ||
### 0.18.1 | ||
- Fixes error when inspecting the sticky header due to cyclical object structure | ||
on `style` (#23) | ||
### 0.18.0 (Compatibility with React Native 0.20.0) | ||
- **Breaking:** Removes `ParallaxScrollView#scrollWithoutAnimationTo` since this has been deprecated in React Native. If you used this method previously, use `scrollTo` instead. | ||
- Adds `ParallaxScrollView#getScrollableNode` method, which is required in React Native 0.20.0 for components implementing | ||
`ScrollView` interface. | ||
See full changelog [here](./CHANGELOG.md). | ||
@@ -105,0 +131,0 @@ |
668
src/index.js
@@ -1,20 +0,15 @@ | ||
import React, { Component } from 'react'; | ||
import { | ||
Animated, | ||
Dimensions, | ||
ScrollView, | ||
View | ||
} from 'react-native'; | ||
import React, { Component } from 'react' | ||
import { Animated, Dimensions, ScrollView, View, ViewPropTypes } from 'react-native' | ||
const styles = require('./styles'); | ||
const styles = require('./styles') | ||
const { bool, func, number, string } = React.PropTypes; | ||
import { bool, func, number, string } from 'prop-types' | ||
const window = Dimensions.get('window'); | ||
const window = Dimensions.get('window') | ||
const SCROLLVIEW_REF = 'ScrollView'; | ||
const SCROLLVIEW_REF = 'ScrollView' | ||
const pivotPoint = (a, b) => (a - b); | ||
const pivotPoint = (a, b) => a - b | ||
const renderEmpty = () => <View/>; | ||
const renderEmpty = () => <View /> | ||
@@ -25,312 +20,405 @@ // Override `toJSON` of interpolated value because of | ||
const interpolate = (value, opts) => { | ||
const x = value.interpolate(opts); | ||
x.toJSON = () => x.__getValue(); | ||
return x; | ||
}; | ||
const x = value.interpolate(opts) | ||
x.toJSON = () => x.__getValue() | ||
return x | ||
} | ||
// Properties accepted by `ParallaxScrollView`. | ||
const IPropTypes = { | ||
backgroundColor: string, | ||
backgroundScrollSpeed: number, | ||
fadeOutForeground: bool, | ||
fadeOutBackground: bool, | ||
contentBackgroundColor: string, | ||
onChangeHeaderVisibility: func, | ||
parallaxHeaderHeight: number.isRequired, | ||
renderBackground: func, | ||
renderFixedHeader: func, | ||
renderForeground: func, | ||
renderScrollComponent: func, | ||
renderStickyHeader: func, | ||
stickyHeaderHeight: number, | ||
contentContainerStyle: View.propTypes.style | ||
}; | ||
backgroundColor: string, | ||
backgroundScrollSpeed: number, | ||
fadeOutForeground: bool, | ||
fadeOutBackground: bool, | ||
contentBackgroundColor: string, | ||
onChangeHeaderVisibility: func, | ||
parallaxHeaderHeight: number.isRequired, | ||
renderBackground: func, | ||
renderFixedHeader: func, | ||
renderForeground: func, | ||
renderScrollComponent: func, | ||
renderStickyHeader: func, | ||
stickyHeaderHeight: number, | ||
contentContainerStyle: ViewPropTypes.style, | ||
outputScaleValue: number | ||
} | ||
class ParallaxScrollView extends Component { | ||
constructor(props) { | ||
super(props); | ||
if (props.renderStickyHeader && !props.stickyHeaderHeight) { | ||
console.warn('Property `stickyHeaderHeight` must be set if `renderStickyHeader` is used.'); | ||
} | ||
if (props.renderParallaxHeader !== renderEmpty && !props.renderForeground) { | ||
console.warn('Property `renderParallaxHeader` is deprecated. Use `renderForeground` instead.'); | ||
} | ||
this.state = { | ||
scrollY: new Animated.Value(0), | ||
viewHeight: window.height, | ||
viewWidth: window.width | ||
}; | ||
this._footerComponent = { setNativeProps() {} }; // Initial stub | ||
this._footerHeight = 0; | ||
} | ||
constructor(props) { | ||
super(props) | ||
if (props.renderStickyHeader && !props.stickyHeaderHeight) { | ||
console.warn( | ||
'Property `stickyHeaderHeight` must be set if `renderStickyHeader` is used.' | ||
) | ||
} | ||
if (props.renderParallaxHeader !== renderEmpty && !props.renderForeground) { | ||
console.warn( | ||
'Property `renderParallaxHeader` is deprecated. Use `renderForeground` instead.' | ||
) | ||
} | ||
this.state = { | ||
scrollY: new Animated.Value(0), | ||
viewHeight: window.height, | ||
viewWidth: window.width | ||
} | ||
this.scrollY = new Animated.Value(0) | ||
this._footerComponent = { setNativeProps() {} } // Initial stub | ||
this._footerHeight = 0 | ||
} | ||
render() { | ||
const { | ||
backgroundColor, | ||
backgroundScrollSpeed, | ||
children, | ||
contentBackgroundColor, | ||
fadeOutForeground, | ||
fadeOutBackground, | ||
parallaxHeaderHeight, | ||
renderBackground, | ||
renderFixedHeader, | ||
renderForeground, | ||
renderParallaxHeader, | ||
renderScrollComponent, | ||
renderStickyHeader, | ||
stickyHeaderHeight, | ||
style, | ||
contentContainerStyle, | ||
...scrollViewProps | ||
} = this.props; | ||
animatedEvent = Animated.event( | ||
[{ nativeEvent: { contentOffset: { y: this.scrollY } } }], | ||
{ useNativeDriver: true } | ||
) | ||
const background = this._renderBackground({ fadeOutBackground, backgroundScrollSpeed, backgroundColor, parallaxHeaderHeight, stickyHeaderHeight, renderBackground }); | ||
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 scrollElement = renderScrollComponent(scrollViewProps); | ||
render() { | ||
const { | ||
backgroundColor, | ||
backgroundScrollSpeed, | ||
children, | ||
contentBackgroundColor, | ||
fadeOutForeground, | ||
fadeOutBackground, | ||
parallaxHeaderHeight, | ||
renderBackground, | ||
renderFixedHeader, | ||
renderForeground, | ||
renderParallaxHeader, | ||
renderScrollComponent, | ||
renderStickyHeader, | ||
stickyHeaderHeight, | ||
style, | ||
contentContainerStyle, | ||
outputScaleValue, | ||
...scrollViewProps | ||
} = this.props | ||
return ( | ||
<View style={[style, styles.container]} | ||
onLayout={(e) => this._maybeUpdateViewDimensions(e)}> | ||
{ background } | ||
{ | ||
React.cloneElement(scrollElement, { | ||
ref: SCROLLVIEW_REF, | ||
style: [styles.scrollView, scrollElement.props.style], | ||
scrollEventThrottle: 16, | ||
onScroll: this._onScroll.bind(this), | ||
}, | ||
foreground, | ||
bodyComponent, | ||
footerSpacer | ||
) | ||
} | ||
{ maybeStickyHeader } | ||
</View> | ||
); | ||
} | ||
const background = this._renderBackground({ | ||
fadeOutBackground, | ||
backgroundScrollSpeed, | ||
backgroundColor, | ||
parallaxHeaderHeight, | ||
stickyHeaderHeight, | ||
renderBackground, | ||
outputScaleValue | ||
}) | ||
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 scrollElement = renderScrollComponent(scrollViewProps) | ||
return ( | ||
<View | ||
style={[style, styles.container]} | ||
onLayout={e => this._maybeUpdateViewDimensions(e)} | ||
> | ||
{background} | ||
{React.cloneElement( | ||
scrollElement, | ||
{ | ||
ref: SCROLLVIEW_REF, | ||
style: [styles.scrollView, scrollElement.props.style], | ||
scrollEventThrottle: 1, | ||
// Using Native Driver greatly optimizes performance | ||
onScroll: Animated.event( | ||
[{ nativeEvent: { contentOffset: { y: this.scrollY } } }], | ||
{ useNativeDriver: true, listener: this._onScroll.bind(this) } | ||
) | ||
// onScroll: this._onScroll.bind(this) | ||
}, | ||
foreground, | ||
bodyComponent, | ||
footerSpacer | ||
)} | ||
{maybeStickyHeader} | ||
</View> | ||
) | ||
} | ||
/* | ||
/* | ||
* Expose `ScrollView` API so this component is composable with any component that expects a `ScrollView`. | ||
*/ | ||
getScrollResponder() { | ||
return this.refs[SCROLLVIEW_REF].getScrollResponder(); | ||
} | ||
getScrollableNode() { | ||
return this.getScrollResponder().getScrollableNode(); | ||
} | ||
getInnerViewNode() { | ||
return this.getScrollResponder().getInnerViewNode(); | ||
} | ||
scrollTo(...args) { | ||
this.getScrollResponder().scrollTo(...args); | ||
} | ||
setNativeProps(props) { | ||
this.refs[SCROLLVIEW_REF].setNativeProps(props); | ||
} | ||
getScrollResponder() { | ||
return this.refs[SCROLLVIEW_REF]._component.getScrollResponder() | ||
} | ||
getScrollableNode() { | ||
return this.getScrollResponder().getScrollableNode() | ||
} | ||
getInnerViewNode() { | ||
return this.getScrollResponder().getInnerViewNode() | ||
} | ||
scrollTo(...args) { | ||
this.getScrollResponder().scrollTo(...args) | ||
} | ||
setNativeProps(props) { | ||
this.refs[SCROLLVIEW_REF].setNativeProps(props) | ||
} | ||
/* | ||
/* | ||
* Private helpers | ||
*/ | ||
_onScroll(e) { | ||
const { | ||
parallaxHeaderHeight, | ||
stickyHeaderHeight, | ||
onChangeHeaderVisibility, | ||
onScroll: prevOnScroll = () => {} | ||
} = this.props; | ||
_onScroll(e) { | ||
const { | ||
parallaxHeaderHeight, | ||
stickyHeaderHeight, | ||
onChangeHeaderVisibility, | ||
onScroll: prevOnScroll = e => {} | ||
} = this.props | ||
const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight); | ||
const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight) | ||
this._maybeUpdateScrollPosition(e); | ||
// This optimization wont run, since we update the animation value directly in onScroll event | ||
// this._maybeUpdateScrollPosition(e) | ||
if (e.nativeEvent.contentOffset.y >= p) { | ||
onChangeHeaderVisibility(false); | ||
} else { | ||
onChangeHeaderVisibility(true); | ||
} | ||
if (e.nativeEvent.contentOffset.y >= p) { | ||
onChangeHeaderVisibility(false) | ||
} else { | ||
onChangeHeaderVisibility(true) | ||
} | ||
prevOnScroll(e); | ||
} | ||
prevOnScroll(e) | ||
} | ||
// This optimizes the state update of current scrollY since we don't need to | ||
// perform any updates when user has scrolled past the pivot point. | ||
_maybeUpdateScrollPosition(e) { | ||
const { parallaxHeaderHeight, stickyHeaderHeight } = this.props; | ||
const { scrollY } = this.state; | ||
const { nativeEvent: { contentOffset: { y: offsetY } } } = e; | ||
const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight); | ||
// This optimizes the state update of current scrollY since we don't need to | ||
// perform any updates when user has scrolled past the pivot point. | ||
_maybeUpdateScrollPosition(e) { | ||
const { parallaxHeaderHeight, stickyHeaderHeight } = this.props | ||
const { scrollY } = this | ||
const { nativeEvent: { contentOffset: { y: offsetY } } } = e | ||
const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight) | ||
if (offsetY <= p || scrollY._value <= p) { | ||
scrollY.setValue(offsetY) | ||
} | ||
} | ||
if (offsetY <= p || scrollY._value <= p) { | ||
scrollY.setValue(offsetY); | ||
} | ||
} | ||
_maybeUpdateViewDimensions(e) { | ||
const { nativeEvent: { layout: { width, height } } } = e | ||
_maybeUpdateViewDimensions(e) { | ||
const { nativeEvent: { layout: { width, height} } } = e; | ||
if (width !== this.state.viewWidth || height !== this.state.viewHeight) { | ||
this.setState({ | ||
viewWidth: width, | ||
viewHeight: height | ||
}) | ||
} | ||
} | ||
if (width !== this.state.viewWidth || height !== this.state.viewHeight) { | ||
this.setState({ | ||
viewWidth: width, | ||
viewHeight: height | ||
}); | ||
} | ||
} | ||
_renderBackground({ | ||
fadeOutBackground, | ||
backgroundScrollSpeed, | ||
backgroundColor, | ||
parallaxHeaderHeight, | ||
stickyHeaderHeight, | ||
renderBackground, | ||
outputScaleValue | ||
}) { | ||
const { viewWidth, viewHeight } = this.state | ||
const { scrollY } = this | ||
const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight) | ||
return ( | ||
<Animated.View | ||
style={[ | ||
styles.backgroundImage, | ||
{ | ||
backgroundColor: backgroundColor, | ||
height: parallaxHeaderHeight, | ||
width: viewWidth, | ||
opacity: fadeOutBackground | ||
? interpolate(scrollY, { | ||
inputRange: [0, p * (1 / 2), p * (3 / 4), p], | ||
outputRange: [1, 0.3, 0.1, 0], | ||
extrapolate: 'clamp' | ||
}) | ||
: 1, | ||
transform: [ | ||
{ | ||
translateY: interpolate(scrollY, { | ||
inputRange: [0, p], | ||
outputRange: [0, -(p / backgroundScrollSpeed)], | ||
extrapolateRight: 'extend', | ||
extrapolateLeft: 'clamp' | ||
}) | ||
}, | ||
{ | ||
scale: interpolate(scrollY, { | ||
inputRange: [-viewHeight, 0], | ||
outputRange: [outputScaleValue, 1], | ||
extrapolate: 'clamp' | ||
}) | ||
} | ||
] | ||
} | ||
]} | ||
> | ||
<View> | ||
{renderBackground()} | ||
</View> | ||
</Animated.View> | ||
) | ||
} | ||
_renderBackground({ fadeOutBackground, backgroundScrollSpeed, backgroundColor, parallaxHeaderHeight, stickyHeaderHeight, renderBackground }) { | ||
const { viewWidth, viewHeight, scrollY } = this.state; | ||
const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight); | ||
return ( | ||
<Animated.View | ||
style={[styles.backgroundImage, { | ||
backgroundColor: backgroundColor, | ||
height: parallaxHeaderHeight, | ||
width: viewWidth, | ||
opacity: fadeOutBackground | ||
? interpolate(scrollY, { | ||
inputRange: [0, p * (1/2), p * (3/4), p], | ||
outputRange: [1, 0.3, 0.1, 0], | ||
extrapolate: 'clamp' | ||
}) | ||
: 1, | ||
transform: [{ | ||
translateY: interpolate(scrollY, { | ||
inputRange: [0, p], | ||
outputRange: [0, -(p / backgroundScrollSpeed)], | ||
extrapolateRight: 'extend', | ||
extrapolateLeft: 'clamp' | ||
}) | ||
}, { | ||
scale: interpolate(scrollY, { | ||
inputRange: [-viewHeight, 0], | ||
outputRange: [5, 1], | ||
extrapolate: 'clamp' | ||
}) | ||
}] | ||
}]}> | ||
<View> | ||
{ renderBackground() } | ||
</View> | ||
</Animated.View> | ||
); | ||
} | ||
_renderForeground({ | ||
fadeOutForeground, | ||
parallaxHeaderHeight, | ||
stickyHeaderHeight, | ||
renderForeground | ||
}) { | ||
const { scrollY } = this | ||
const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight) | ||
return ( | ||
<View style={styles.parallaxHeaderContainer}> | ||
<Animated.View | ||
style={[ | ||
styles.parallaxHeader, | ||
{ | ||
height: parallaxHeaderHeight, | ||
opacity: fadeOutForeground | ||
? interpolate(scrollY, { | ||
inputRange: [0, p * (1 / 2), p * (3 / 4), p], | ||
outputRange: [1, 0.3, 0.1, 0], | ||
extrapolate: 'clamp' | ||
}) | ||
: 1 | ||
} | ||
]} | ||
> | ||
<View style={{ height: parallaxHeaderHeight }}> | ||
{renderForeground()} | ||
</View> | ||
</Animated.View> | ||
</View> | ||
) | ||
} | ||
_renderForeground({ fadeOutForeground, parallaxHeaderHeight, stickyHeaderHeight, renderForeground }) { | ||
const { scrollY } = this.state; | ||
const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight); | ||
return ( | ||
<View style={styles.parallaxHeaderContainer}> | ||
<Animated.View | ||
style={[styles.parallaxHeader, { | ||
height: parallaxHeaderHeight, | ||
opacity: fadeOutForeground | ||
? interpolate(scrollY, { | ||
inputRange: [0, p * (1/2), p * (3/4), p], | ||
outputRange: [1, 0.3, 0.1, 0], | ||
extrapolate: 'clamp' | ||
}) | ||
: 1 | ||
}]}> | ||
<View style={{ height: parallaxHeaderHeight }}> | ||
{ renderForeground() } | ||
</View> | ||
</Animated.View> | ||
</View> | ||
); | ||
} | ||
_wrapChildren( | ||
children, | ||
{ contentBackgroundColor, stickyHeaderHeight, contentContainerStyle } | ||
) { | ||
const { viewHeight } = this.state | ||
const containerStyles = [{ backgroundColor: contentBackgroundColor }] | ||
_wrapChildren(children, { contentBackgroundColor, stickyHeaderHeight, contentContainerStyle }) { | ||
const { viewHeight } = this.state; | ||
const containerStyles = [{backgroundColor: contentBackgroundColor}]; | ||
if (contentContainerStyle) containerStyles.push(contentContainerStyle) | ||
if(contentContainerStyle) | ||
containerStyles.push(contentContainerStyle); | ||
return ( | ||
<View | ||
style={containerStyles} | ||
onLayout={e => { | ||
// 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} | ||
</View> | ||
) | ||
} | ||
return ( | ||
<View | ||
style={containerStyles} | ||
onLayout={e => { | ||
// 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 } | ||
</View> | ||
); | ||
} | ||
_renderFooterSpacer({ contentBackgroundColor }) { | ||
return ( | ||
<View | ||
ref={ref => (this._footerComponent = ref)} | ||
style={{ backgroundColor: contentBackgroundColor }} | ||
/> | ||
) | ||
} | ||
_renderFooterSpacer({ contentBackgroundColor }) { | ||
return ( | ||
<View ref={ref => this._footerComponent = ref } style={{ backgroundColor: contentBackgroundColor }}/> | ||
); | ||
} | ||
_maybeRenderStickyHeader({ parallaxHeaderHeight, stickyHeaderHeight, backgroundColor, renderFixedHeader, renderStickyHeader }) { | ||
const { viewWidth, scrollY } = this.state; | ||
if (renderStickyHeader || renderFixedHeader) { | ||
const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight); | ||
return ( | ||
<View style={[styles.stickyHeader, { width: viewWidth, ...(stickyHeaderHeight ? { height: stickyHeaderHeight } : null ) }]}> | ||
{ | ||
renderStickyHeader | ||
? ( | ||
<Animated.View | ||
style={{ | ||
backgroundColor: backgroundColor, | ||
height: stickyHeaderHeight, | ||
opacity: interpolate(scrollY, { | ||
inputRange: [0, p], | ||
outputRange: [0, 1], | ||
extrapolate: 'clamp' | ||
}) | ||
}}> | ||
<Animated.View | ||
style={{ | ||
transform: [{ | ||
translateY: interpolate(scrollY, { | ||
inputRange: [0, p], | ||
outputRange: [stickyHeaderHeight, 0], | ||
extrapolate: 'clamp' | ||
}) | ||
}] | ||
}}> | ||
{ renderStickyHeader() } | ||
</Animated.View> | ||
</Animated.View> | ||
) | ||
: null | ||
} | ||
{ renderFixedHeader && renderFixedHeader() } | ||
</View> | ||
); | ||
} else { | ||
return null; | ||
} | ||
} | ||
_maybeRenderStickyHeader({ | ||
parallaxHeaderHeight, | ||
stickyHeaderHeight, | ||
backgroundColor, | ||
renderFixedHeader, | ||
renderStickyHeader | ||
}) { | ||
const { viewWidth } = this.state | ||
const { scrollY } = this | ||
if (renderStickyHeader || renderFixedHeader) { | ||
const p = pivotPoint(parallaxHeaderHeight, stickyHeaderHeight) | ||
return ( | ||
<View | ||
style={[ | ||
styles.stickyHeader, | ||
{ | ||
width: viewWidth, | ||
...(stickyHeaderHeight ? { height: stickyHeaderHeight } : null) | ||
} | ||
]} | ||
> | ||
{renderStickyHeader | ||
? <Animated.View | ||
style={{ | ||
backgroundColor: backgroundColor, | ||
height: stickyHeaderHeight, | ||
opacity: interpolate(scrollY, { | ||
inputRange: [0, p], | ||
outputRange: [0, 1], | ||
extrapolate: 'clamp' | ||
}) | ||
}} | ||
> | ||
<Animated.View | ||
style={{ | ||
transform: [ | ||
{ | ||
translateY: interpolate(scrollY, { | ||
inputRange: [0, p], | ||
outputRange: [stickyHeaderHeight, 0], | ||
extrapolate: 'clamp' | ||
}) | ||
} | ||
] | ||
}} | ||
> | ||
{renderStickyHeader()} | ||
</Animated.View> | ||
</Animated.View> | ||
: null} | ||
{renderFixedHeader && renderFixedHeader()} | ||
</View> | ||
) | ||
} else { | ||
return null | ||
} | ||
} | ||
} | ||
ParallaxScrollView.propTypes = IPropTypes; | ||
ParallaxScrollView.propTypes = IPropTypes | ||
ParallaxScrollView.defaultProps = { | ||
backgroundScrollSpeed: 5, | ||
backgroundColor: '#000', | ||
contentBackgroundColor: '#fff', | ||
fadeOutForeground: true, | ||
onChangeHeaderVisibility: () => {}, | ||
renderScrollComponent: props => <ScrollView {...props}/>, | ||
renderBackground: renderEmpty, | ||
renderParallaxHeader: renderEmpty, // Deprecated (will be removed in 0.18.0) | ||
renderForeground: null, | ||
stickyHeaderHeight: 0, | ||
contentContainerStyle: null | ||
}; | ||
backgroundScrollSpeed: 5, | ||
backgroundColor: '#000', | ||
contentBackgroundColor: '#fff', | ||
fadeOutForeground: true, | ||
onChangeHeaderVisibility: () => {}, | ||
renderScrollComponent: props => <Animated.ScrollView {...props} />, | ||
renderBackground: renderEmpty, | ||
renderParallaxHeader: renderEmpty, // Deprecated (will be removed in 0.18.0) | ||
renderForeground: null, | ||
stickyHeaderHeight: 0, | ||
contentContainerStyle: null, | ||
outputScaleValue: 5 | ||
} | ||
module.exports = ParallaxScrollView; | ||
module.exports = ParallaxScrollView |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
20621
423
136
1
1
+ Addedprop-types@^15.6.0
+ Addedjs-tokens@4.0.0(transitive)
+ Addedloose-envify@1.4.0(transitive)
+ Addedobject-assign@4.1.1(transitive)
+ Addedprop-types@15.8.1(transitive)
+ Addedreact-is@16.13.1(transitive)