diff --git a/demo/Demo.js b/demo/Demo.js
index d3cba75..7be9409 100644
--- a/demo/Demo.js
+++ b/demo/Demo.js
@@ -12,9 +12,7 @@ import ButtonDemo from './demos/ButtonDemo';
import ViewPagerDemo from './demos/ViewPagerDemo';
import TabBarDemo from './demos/TabBarDemo';
import MediaKitDemo from './demos/MediaKitDemo';
-
-
-
+import ParallaxViewDemo from './demos/ParallaxViewDemo';
export default class Demo extends Component {
render() {
@@ -57,5 +55,8 @@ export default class Demo extends Component {
if(route.name === 'MediaKit') {
return
}
+ if(route.name === 'ParallaxView') {
+ return
+ }
}
}
diff --git a/demo/demos/DemoList.js b/demo/demos/DemoList.js
index dfade4f..93859ba 100644
--- a/demo/demos/DemoList.js
+++ b/demo/demos/DemoList.js
@@ -42,7 +42,12 @@ const dataSource = ds.cloneWithRowsAndSections({
{
name: 'MediaKit',
desc: '媒体播放'
- },],
+ },
+ {
+ name: 'ParallaxView',
+ desc: '视差效果'
+ },
+ ],
'Control Components': [
{
name: 'RefreshControl',
diff --git a/demo/demos/ParallaxViewDemo.js b/demo/demos/ParallaxViewDemo.js
new file mode 100644
index 0000000..764f0af
--- /dev/null
+++ b/demo/demos/ParallaxViewDemo.js
@@ -0,0 +1,152 @@
+'use strict';
+
+import React, {Component} from 'react';
+import {
+ StyleSheet,
+ Text,
+ View,
+ ScrollView,
+ Dimensions,
+ PixelRatio,
+ Animated,
+} from 'react-native';
+
+import {ParallaxView} from 'react-native-yui';
+
+let WINDOW_WIDTH = Dimensions.get('window').width;
+let WINDOW_HEIGHT = Dimensions.get('window').height;
+let IMAGE_HEIGHT = WINDOW_WIDTH / 1.5;
+let PIXEL_RATIO = PixelRatio.get();
+let PARALLAX_FACTOR = 0.5;
+
+let SECTIONS = [
+ {
+ title: '隐形舒适,美不留痕',
+ source:require('../jpg/tampon0.jpg')
+ },
+ {
+ title: '更IN,更美,更轻松',
+ source:require('../jpg/tampon1.jpg')
+ },
+ {
+ title: '随心而动,精彩不停',
+ source:require('../jpg/tampon2.jpg')
+ },
+ {
+ title: '完美细节,时刻贴心',
+ source:require('../jpg/tampon3.jpg')
+ },
+ {
+ title: '定位准,易置入',
+ source:require('../jpg/tampon4.jpg')
+ },
+ {
+ title: '丝缎般光滑触感',
+ source:require('../jpg/tampon5.jpg')
+ },
+ {
+ title: 'WCM世界级制造标准',
+ source:require('../jpg/tampon6.jpg')
+ },
+ {
+ title: '反复打磨细节之处',
+ source:require('../jpg/tampon7.jpg')
+ },
+ {
+ title: '选取最优质材料',
+ source:require('../jpg/tampon8.jpg')
+ },
+ {
+ title: '配送更快更安心',
+ source:require('../jpg/tampon9.jpg')
+ }
+];
+
+export default class DemoSection1 extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ horizontal: false,
+ pagingEnabled:false
+ }
+ }
+
+ onPress() {
+ this.setState({
+ horizontal:!this.state.horizontal,
+ pagingEnabled: !this.state.pagingEnabled
+ })
+ }
+
+ render() {
+ let parallaxViewStyle = {
+ height: this.state.horizontal ? WINDOW_HEIGHT - 100 : IMAGE_HEIGHT,
+ width: WINDOW_WIDTH,
+ marginTop: 10,
+ marginRight : this.state.horizontal? 10:0
+ };
+
+ let content = (
+ SECTIONS.map((section, i) =>(
+
+ {section.title}
+ Source: {'www.yoai.com'}
+
+ ))
+ );
+
+ return (
+
+
+
+ { this.state.horizontal ? '水平方向' : '垂直方向'}
+
+
+
+ {content}
+
+
+ );
+ }
+};
+
+var styles = StyleSheet.create({
+ overlay: {
+ alignItems: 'center',
+ justifyContent: 'center',
+ backgroundColor: 'rgba(0,0,0,0.3)',
+ },
+ title: {
+ fontSize: 20,
+ textAlign: 'center',
+ lineHeight: 25,
+ fontWeight: 'bold',
+ color: 'white',
+ shadowOffset: {
+ width: 0,
+ height: 0,
+ },
+ shadowRadius: 1,
+ shadowColor: 'black',
+ shadowOpacity: 0.8,
+ },
+ url: {
+ opacity: 0.5,
+ fontSize: 10,
+ position: 'absolute',
+ color: 'white',
+ left: 5,
+ bottom: 5,
+ }
+});
diff --git a/demo/jpg/tampon0.jpg b/demo/jpg/tampon0.jpg
new file mode 100644
index 0000000..4cd1321
Binary files /dev/null and b/demo/jpg/tampon0.jpg differ
diff --git a/demo/jpg/tampon1.jpg b/demo/jpg/tampon1.jpg
new file mode 100644
index 0000000..3c37cff
Binary files /dev/null and b/demo/jpg/tampon1.jpg differ
diff --git a/demo/jpg/tampon2.jpg b/demo/jpg/tampon2.jpg
new file mode 100644
index 0000000..626ff24
Binary files /dev/null and b/demo/jpg/tampon2.jpg differ
diff --git a/demo/jpg/tampon3.jpg b/demo/jpg/tampon3.jpg
new file mode 100644
index 0000000..1162f75
Binary files /dev/null and b/demo/jpg/tampon3.jpg differ
diff --git a/demo/jpg/tampon4.jpg b/demo/jpg/tampon4.jpg
new file mode 100644
index 0000000..f0e9f09
Binary files /dev/null and b/demo/jpg/tampon4.jpg differ
diff --git a/demo/jpg/tampon5.jpg b/demo/jpg/tampon5.jpg
new file mode 100644
index 0000000..c9a368a
Binary files /dev/null and b/demo/jpg/tampon5.jpg differ
diff --git a/demo/jpg/tampon6.jpg b/demo/jpg/tampon6.jpg
new file mode 100644
index 0000000..d6ccee7
Binary files /dev/null and b/demo/jpg/tampon6.jpg differ
diff --git a/demo/jpg/tampon7.jpg b/demo/jpg/tampon7.jpg
new file mode 100644
index 0000000..4de92ef
Binary files /dev/null and b/demo/jpg/tampon7.jpg differ
diff --git a/demo/jpg/tampon8.jpg b/demo/jpg/tampon8.jpg
new file mode 100644
index 0000000..e28344d
Binary files /dev/null and b/demo/jpg/tampon8.jpg differ
diff --git a/demo/jpg/tampon9.jpg b/demo/jpg/tampon9.jpg
new file mode 100644
index 0000000..85c8167
Binary files /dev/null and b/demo/jpg/tampon9.jpg differ
diff --git a/index.js b/index.js
index ea6fcb0..8a9464e 100755
--- a/index.js
+++ b/index.js
@@ -59,7 +59,10 @@ var YUI = {
},
get DatePicker() {
return ReactNative.DatePicker;
+ },
+ get ParallaxView() {
+ return require('./library/parallax/ParallaxView').default;
}
-}
+};
module.exports = YUI;
diff --git a/library/parallax/ParallaxScrollView.js b/library/parallax/ParallaxScrollView.js
new file mode 100644
index 0000000..02fba57
--- /dev/null
+++ b/library/parallax/ParallaxScrollView.js
@@ -0,0 +1,66 @@
+import React, {Component, PropTypes} from 'react';
+import {
+ Animated,
+ ScrollView
+} from 'react-native';
+
+import ParallaxView from './ParallaxView';
+
+
+var applyPropsToParallaxImages = function (children, props) {
+
+ if (children instanceof Array) {
+ return children.map(child => {
+ if (child instanceof Array) {
+ return applyPropsToParallaxImages(child, props);
+ }
+ if (child.type === ParallaxView) {
+ return React.cloneElement(child, props);
+ }
+ return child;
+ });
+ }
+ if (children.type === ParallaxView) {
+ return React.cloneElement(children, props);
+ }
+ return children;
+};
+
+
+export default class ParallaxScrollView extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {}
+ }
+
+ componentWillMount() {
+ var scrollValue = new Animated.Value(0);
+ this.setState({scrollValue});
+ }
+
+ onParallaxScroll(event) {
+ const {nativeEvent: {contentOffset: {y: offsetY, x: offsetX}}} = event;
+ this.state.scrollValue.setValue( this.props.horizontal ? offsetX : offsetY);
+ }
+
+ render() {
+ let {children, onScroll, horizontal, ...props} = this.props;
+ let {scrollValue} = this.state;
+ let handleScroll = (onScroll
+ ? event => { this.onParallaxScroll(event); onScroll(event); }
+ : this.onParallaxScroll
+ );
+ children = children && applyPropsToParallaxImages(children, {scrollValue, horizontal});
+ return (
+
+ {children}
+
+ )
+ }
+}
diff --git a/library/parallax/ParallaxView.js b/library/parallax/ParallaxView.js
new file mode 100644
index 0000000..2ae2ed1
--- /dev/null
+++ b/library/parallax/ParallaxView.js
@@ -0,0 +1,153 @@
+import React, {Component, PropTypes} from 'react';
+
+import {
+ View,
+ Image,
+ Animated,
+ StyleSheet,
+ TouchableOpacity,
+ Text,
+ Dimensions,
+} from 'react-native';
+
+const WINDOW_HEIGHT = Dimensions.get('window').height;
+const WINDOW_WIDTH = Dimensions.get('window').width;
+
+export default class ParallaxView extends Component {
+
+ static get ScrollView() {
+ return require('./ParallaxScrollView').default;
+ }
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ width: 0,
+ height: 0,
+ offsetX: 0,
+ offsetY: 0
+ };
+ }
+
+ handleLayout() {
+ (this._touchable || this._container).measure(this.handleMeasure.bind(this));
+ }
+
+ handleMeasure(ox, oy, width, height, px, py) {
+ this.setState({
+ offsetY: py,
+ offsetX: px,
+ height,
+ width
+ });
+ }
+
+ render() {
+ let {height, width, offsetX, offsetY} = this.state;
+ let {
+ scrollValue,
+ parallaxFactor,
+ style,
+ children,
+ overlayStyle,
+ imageStyle,
+ horizontal,
+ onPress,
+ ...props
+ } = this.props;
+
+ let parallaxPadding = horizontal ? width * parallaxFactor : height * parallaxFactor;
+
+ let parallaxStyle = {
+ height: horizontal ? height : height + parallaxPadding * 2,
+ width: !horizontal ? width : width + parallaxPadding * 2
+ };
+
+ if (scrollValue) {
+ parallaxStyle.transform = [
+ horizontal
+ ? {translateY: 0}
+ : {
+ translateY: scrollValue.interpolate({
+ inputRange: [offsetY - height, offsetY + WINDOW_HEIGHT + height],
+ outputRange: [-parallaxPadding, parallaxPadding]
+ })
+ },
+ !horizontal
+ ? {translateX: 0}
+ : {
+ translateX: scrollValue.interpolate({
+ inputRange: [offsetX - width, offsetX + WINDOW_WIDTH + width],
+ outputRange: [-parallaxPadding, parallaxPadding]
+ })
+ }
+ ]
+ } else {
+ parallaxStyle.transform = [horizontal ? {translateY: -parallaxPadding} : {translateX: -parallaxPadding}]
+ }
+ let content = (
+ {this._container = ref}}
+ style={[style,styles.container]}
+ onLayout={this.handleLayout.bind(this)}
+ >
+
+
+ {children}
+
+
+ );
+ if (onPress) {
+ return (
+ {this._touchable = ref}}
+ >
+ {content}
+
+ )
+ }
+ return content;
+ }
+}
+
+var styles = StyleSheet.create({
+ container: {
+ overflow: 'hidden',
+ position: 'relative'
+ },
+ overlay: {
+ flex: 1,
+ top: 0,
+ left: 0,
+ right: 0,
+ bottom: 0,
+ position: 'absolute'
+ }
+});
+
+ParallaxView.propsTypes = {
+ style: PropTypes.oneOfType([
+ PropTypes.object,
+ PropTypes.number
+ ]),
+ parallaxFactor : PropTypes.number,
+ horizontal: PropTypes.bool,
+ overlayStyle: PropTypes.oneOfType([
+ PropTypes.object,
+ PropTypes.number
+ ]),
+ source: PropTypes.oneOfType([
+ PropTypes.shape({
+ uri: PropTypes.string
+ }),
+ PropTypes.number
+ ])
+};
+ParallaxView.defaultProps = {
+ horizontal: false,
+ parallaxFactor : 0.3
+};
\ No newline at end of file