diff --git a/.gitignore b/.gitignore index fb48be7..d3cc3b0 100644 --- a/.gitignore +++ b/.gitignore @@ -26,26 +26,4 @@ build/Release # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules -.idea/.name - -.idea/encodings.xml - -.idea/inspectionProfiles/profiles_settings.xml - -.idea/inspectionProfiles/Project_Default.xml - -.idea/misc.xml - -.idea/modules.xml - -.idea/react-native-parallax-view.iml - -.idea/scopes/scope_settings.xml - -.idea/vcs.xml - -.idea/workspace.xml - -.idea/libraries/react_native_parallax_view_node_modules.xml - -.idea/jsLibraryMappings.xml +.idea \ No newline at end of file diff --git a/README.md b/README.md index c930311..6ee4f28 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +**DEPRECATED - I will no longer maintain this repo. Consider using [https://github.com/dejakob/react-native-keyboard-aware-parallax-scroll-view](https://github.com/dejakob/react-native-keyboard-aware-parallax-scroll-view)** + # react-native-parallax-view Parallax view for vertical scrollview with a header image and header content @@ -49,7 +51,7 @@ Additionally, here is an example of the usage | Prop | Required | Default | Type | Description | | :------------ |:---:|:---------------:| :---------------:| :-----| -| backgroundSource | YES | `null` | `object` | the `source` prop that get's passed to the background `` component. If left blank, no background is rendered | +| background | YES | `null` | `object` | Background can be image string, image object (with uri) or a React component to render | | header | NO | `null` | `renderable` | any content you want to render on top of the image. This content's opacity get's animated down as the scrollview scrolls up. (optional) | | windowHeight | NO | `300` | `number` | the resting height of the header image. If 0 is passed in, the background is not rendered. | | scrollableViewStyle | NO | `null` | `object` | this style will be mixed (overriding existing fields) with scrollable view style (view which is scrolled over the background) | diff --git a/index.js b/index.js index 6c032cf..dfb37d4 100644 --- a/index.js +++ b/index.js @@ -1 +1,3 @@ -module.exports = require('./lib/ParallaxView'); \ No newline at end of file +import ParallaxView from './lib/ParallaxView'; + +export default ParallaxView; \ No newline at end of file diff --git a/lib/ParallaxView.js b/lib/ParallaxView.js index 776bdb3..5fdc2df 100644 --- a/lib/ParallaxView.js +++ b/lib/ParallaxView.js @@ -1,142 +1,218 @@ -'use strict'; - -var React = require('react'); -var ReactNative = require('react-native'); -var { - Dimensions, - StyleSheet, - View, - ScrollView, - Animated, - } = ReactNative; -/** - * BlurView temporarily removed until semver stuff is set up properly - */ -//var BlurView /* = require('react-native-blur').BlurView */; -var ScrollableMixin = require('react-native-scrollable-mixin'); -var screen = Dimensions.get('window'); -var ScrollViewPropTypes = ScrollView.propTypes; +import React, { Component } from 'react'; +import { Dimensions, StyleSheet, View, ScrollView, Animated } from 'react-native'; +import ScrollableMixin from 'react-native-scrollable-mixin'; -var ParallaxView = React.createClass({ - mixins: [ScrollableMixin], +const screen = Dimensions.get('window'); +const ScrollViewPropTypes = ScrollView.propTypes; - propTypes: { +/** + * + * + * @property {Number} windowHeight + * @property {Node|String|{ uri: String}} background + * @property {Node} [header] + * @property {Object} contentInset + * + */ +class ParallaxView extends Component +{ + static propTypes = { ...ScrollViewPropTypes, windowHeight: React.PropTypes.number, - backgroundSource: React.PropTypes.oneOfType([ - React.PropTypes.shape({ - uri: React.PropTypes.string, - }), - // Opaque type returned by require('./image.jpg') - React.PropTypes.number, - ]), - header: React.PropTypes.node, - blur: React.PropTypes.string, + background: React.PropTypes.object, + header: React.PropTypes.node.optional, contentInset: React.PropTypes.object, - }, + scrollableViewStyle: React.PropTypes.object + }; - getDefaultProps: function () { - return { + constructor() { + const defaultProps = { windowHeight: 300, - contentInset: { - top: screen.scale - } + contentInset: { top: screen.scale } }; - }, - getInitialState: function () { - return { + super(defaultProps); + + // Initial props + this.props = defaultProps; + + // Initial state + this.state = { scrollY: new Animated.Value(0) }; - }, + + this.handleScroll = this.handleScroll.bind(this); + } /** - * IMPORTANT: You must return the scroll responder of the underlying - * scrollable component from getScrollResponder() when using ScrollableMixin. + * getScrollResponder method for ScrollableMixin + * @returns {ScrollView} */ getScrollResponder() { - return this._scrollView.getScrollResponder(); - }, + return this._scrollView.getScrollResponder(); + } + /** + * setNativeProps method for ScrollableMixin + * @param {Object} props + */ setNativeProps(props) { - this._scrollView.setNativeProps(props); - }, + this._scrollView.setNativeProps(props); + } - renderBackground: function () { - var { windowHeight, backgroundSource, blur } = this.props; - var { scrollY } = this.state; - if (!windowHeight || !backgroundSource) { - return null; + /** + * On each scroll, set the content offset + */ + handleScroll(eventData) { + const events = [ + { + nativeEvent: { + contentOffset: { + y: this.state.scrollY + } + } + } + ]; + const config = {}; + + if (typeof this.props.onScroll === 'function') { + config.listener = eventData => this.props.onScroll(eventData) } + + return Animated.event(events, config); + } + + /** + * Render the Parallax view + * @returns {XML} + */ + render() { + const { style, header, windowHeight, scrollableViewStyle } = this.props; + + const noHeaderStyle = { + paddingTop: windowHeight + }; + return ( - - {/* - !!blur && (BlurView || (BlurView = require('react-native-blur').BlurView)) && - - */} - + + {this.renderBackground()} + + this._scrollView = component} + style={[ styles.scrollView, header ? {} : noHeaderStyle ]} + onScroll={this.handleScroll()} + scrollEventThrottle={16} + > + {this.renderHeader()} + + + {this.props.children} + + + ); - }, + } - renderHeader: function () { - var { windowHeight, backgroundSource } = this.props; - var { scrollY } = this.state; - if (!windowHeight || !backgroundSource) { + /** + * Renders the background component / background image + * @returns {Node} + */ + renderBackground() { + const { windowHeight, background } = this.props; + const { scrollY } = this.state; + + if (!windowHeight || !background) { return null; } + + const style = { + height: windowHeight, + transform: [ + { + translateY: scrollY.interpolate({ + inputRange: [ -windowHeight, 0, windowHeight], + outputRange: [ windowHeight / 2, 0, -windowHeight / 3 ] + }) + }, + { + scale: scrollY.interpolate({ + inputRange: [ -windowHeight, 0, windowHeight ], + outputRange: [ 2, 1, 1 ] + }) + } + ] + }; + + if (typeof background === 'string') { + return ( + + ); + } + + if ( + typeof background === 'object' && + background !== null && + typeof background.uri === 'string' + ) { + return ( + + ); + } + return ( - - {this.props.header} + + {React.cloneElement(background, { style })} - ); - }, + ) + } + + /** + * Render the header (on top of the background) + * @returns {Node} + */ + renderHeader() { + const { windowHeight, background, header } = this.props; + const { scrollY } = this.state; + + if (!windowHeight || !background || !header) { + return null; + } + + const style = { + position: 'relative', + height: windowHeight, + opacity: scrollY.interpolate({ + inputRange: [ -windowHeight, 0, windowHeight / 1.2 ], + outputRange: [1, 1, 0] + }), + }; - render: function () { - var { style, ...props } = this.props; return ( - - {this.renderBackground()} - { this._scrollView = component; }} - {...props} - style={styles.scrollView} - onScroll={Animated.event( - [{ nativeEvent: { contentOffset: { y: this.state.scrollY }}}] - )} - scrollEventThrottle={16}> - {this.renderHeader()} - - {this.props.children} - - - + + {header} + ); } -}); +} -var styles = StyleSheet.create({ +const styles = StyleSheet.create({ container: { flex: 1, borderColor: 'transparent', @@ -147,16 +223,7 @@ var styles = StyleSheet.create({ background: { position: 'absolute', backgroundColor: '#2e2f31', - width: screen.width, - resizeMode: 'cover' - }, - blur: { - position: 'absolute', - left: 0, - right: 0, - top: 0, - bottom: 0, - backgroundColor: 'transparent', + width: screen.width }, content: { shadowColor: '#222', @@ -168,4 +235,6 @@ var styles = StyleSheet.create({ } }); -module.exports = ParallaxView; +Object.assign(ParallaxView.prototype, ScrollableMixin); + +export default ParallaxView;