diff --git a/demo/Demo.js b/demo/Demo.js
index d3cba75..4c57783 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 CenterContent from './demos/CenterContent';
export default class Demo extends Component {
render() {
@@ -56,6 +54,8 @@ export default class Demo extends Component {
}
if(route.name === 'MediaKit') {
return
+ }if(route.name === 'CenterCell') {
+ return
}
}
}
diff --git a/demo/demos/CenterContent.js b/demo/demos/CenterContent.js
new file mode 100644
index 0000000..7c3a1ba
--- /dev/null
+++ b/demo/demos/CenterContent.js
@@ -0,0 +1,143 @@
+'use strict';
+
+import React, {Component, PropTypes} from 'react';
+import {
+ Image,
+ View,
+ Text,
+ PixelRatio,
+ ScrollView
+} from 'react-native';
+
+// import {CenterContentView} from 'react-native-yui';
+import CenterContentView from 'react-native-center-content-view';
+
+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 Demo extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ scale: 0.8,
+ opacity: 1,
+ rotateLeft: "0deg",
+ rotateRight: "0deg",
+ type: "Scale"
+ }
+ }
+
+ renderCell(data) {
+ return (
+
+
+
+ )
+ }
+
+ handleOnPress() {
+ if (this.state.type == "Scale") {
+ this.setState({
+ scale: 1,
+ opacity: 0.3,
+ type: "Opacity"
+ })
+ } else if (this.state.type == "Opacity") {
+ this.setState({
+ opacity: 1,
+ rotateLeft: "-20deg",
+ rotateRight: "20deg",
+ type: "Rotate"
+ })
+ } else if (this.state.type == "Rotate") {
+ this.setState({
+ scale: 0.8,
+ rotateLeft: "0deg",
+ rotateRight: "0deg",
+ type: "Scale"
+ })
+ }
+ }
+
+ render() {
+ return (
+
+
+
+ CenterContentViewDemo
+
+
+
+
+
+
+
+ {this.state.type}
+
+
+
+ )
+ }
+}
+
+
diff --git a/demo/demos/DemoList.js b/demo/demos/DemoList.js
index dfade4f..c4fc844 100644
--- a/demo/demos/DemoList.js
+++ b/demo/demos/DemoList.js
@@ -42,7 +42,12 @@ const dataSource = ds.cloneWithRowsAndSections({
{
name: 'MediaKit',
desc: '媒体播放'
- },],
+ },
+ {
+ name: 'CenterCell',
+ desc: '自动将Cell置于屏幕中间'
+ },
+ ],
'Control Components': [
{
name: 'RefreshControl',
diff --git a/demo/demos/PanTest.js b/demo/demos/PanTest.js
new file mode 100644
index 0000000..3ed10c1
--- /dev/null
+++ b/demo/demos/PanTest.js
@@ -0,0 +1,133 @@
+import React, {Component} from 'react';
+import {View, PanResponder, Animated} from 'react-native';
+
+
+export default class PanTest extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ offSetValue: new Animated.ValueXY(),
+ offSetValue1: new Animated.ValueXY()
+ }
+ }
+
+ componentWillMount() {
+ this._panResponder = PanResponder.create({
+ // 要求成为响应者:
+ onStartShouldSetPanResponder: (evt, gestureState) => true,
+ onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
+ onMoveShouldSetPanResponder: (evt, gestureState) => true,
+ onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
+
+ onPanResponderGrant: (evt, gestureState) => {
+ // 开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!
+
+ // gestureState.{x,y}0 现在会被设置为0
+ this.state.offSetValue.setOffset({x: this.state.offSetValue.x._value, y: this.state.offSetValue.y._value});
+ this.state.offSetValue.setValue({x: 0, y: 0});
+
+ },
+ onPanResponderMove: Animated.event([null, {
+ dx: this.state.offSetValue.x,
+ dy: this.state.offSetValue.y
+ }]),
+ onPanResponderTerminationRequest: (evt, gestureState) => true,
+ onPanResponderRelease: (evt, gestureState) => {
+ this.state.offSetValue.flattenOffset();
+ // 用户放开了所有的触摸点,且此时视图已经成为了响应者。
+ // 一般来说这意味着一个手势操作已经成功完成。
+ },
+ onPanResponderTerminate: (evt, gestureState) => {
+ // 另一个组件已经成为了新的响应者,所以当前手势将被取消。
+ },
+ onShouldBlockNativeResponder: (evt, gestureState) => {
+ // 返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者
+ // 默认返回true。目前暂时只支持android。
+ return true;
+ },
+ });
+
+
+ this._panResponder1 = PanResponder.create({
+ // 要求成为响应者:
+ onStartShouldSetPanResponder: (evt, gestureState) => true,
+ onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
+ onMoveShouldSetPanResponder: (evt, gestureState) => true,
+ onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
+
+ onPanResponderGrant: (evt, gestureState) => {
+ // 开始手势操作。给用户一些视觉反馈,让他们知道发生了什么事情!
+
+ // gestureState.{x,y}0 现在会被设置为0
+ this.state.offSetValue1.setOffset({x: this.state.offSetValue1.x._value, y: this.state.offSetValue1.y._value});
+ this.state.offSetValue1.setValue({x: 0, y: 0});
+
+ },
+ onPanResponderMove: Animated.event([null, {
+ dx: this.state.offSetValue1.x,
+ dy: this.state.offSetValue1.y
+ }]),
+ onPanResponderTerminationRequest: (evt, gestureState) => true,
+ onPanResponderRelease: (evt, gestureState) => {
+ this.state.offSetValue1.flattenOffset();
+ // 用户放开了所有的触摸点,且此时视图已经成为了响应者。
+ // 一般来说这意味着一个手势操作已经成功完成。
+ },
+ onPanResponderTerminate: (evt, gestureState) => {
+ // 另一个组件已经成为了新的响应者,所以当前手势将被取消。
+ },
+ onShouldBlockNativeResponder: (evt, gestureState) => {
+ // 返回一个布尔值,决定当前组件是否应该阻止原生组件成为JS响应者
+ // 默认返回true。目前暂时只支持android。
+ return true;
+ },
+ });
+ }
+
+ render() {
+
+ let {offSetValue,offSetValue1} = this.state;
+
+ let [translateX,translateY] = [offSetValue.x, offSetValue.y];
+
+ let scale = offSetValue.x.interpolate({
+ inputRange: [-100000,-100,0,100,100000],
+ outputRange: [0.8,0.8,1,0.8,0.8]
+ });
+
+ let animatedStyle = {transform: [{translateX}, {translateY},{scale}]};
+
+ let [translateX1,translateY1] = [offSetValue1.x, offSetValue1.y];
+
+ let scale1 = offSetValue.x.interpolate({
+ inputRange: [-100000,-200,-100,0,100000],
+ outputRange: [0.8,0.8,1,0.8,0.8]
+ });
+ let animatedStyle1 = {transform: [{translateX}, {translateY},{scale:scale1}]};
+
+ // let animatedStyle1 = {transform: [{translateX:translateX1}, {translateY:translateY1},{scale:scale1}]};
+
+ return (
+
+
+
+
+
+
+
+
+
+ )
+ }
+}
\ No newline at end of file
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..16950cf 100755
--- a/index.js
+++ b/index.js
@@ -59,7 +59,10 @@ var YUI = {
},
get DatePicker() {
return ReactNative.DatePicker;
+ },
+ get CenterCell() {
+ return require('./library/CenterCell').default;
}
-}
+};
module.exports = YUI;
diff --git a/library/CenterCell.js b/library/CenterCell.js
new file mode 100644
index 0000000..7c72eed
--- /dev/null
+++ b/library/CenterCell.js
@@ -0,0 +1,196 @@
+'use strict';
+
+import React, {Component, PropTypes} from 'react';
+import {
+ StyleSheet,
+ View,
+ Animated,
+ PanResponder,
+} from 'react-native';
+
+let CELL_SPACE = 30;
+let DURATION = 200;
+
+export default class CenterContentView extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ width: 0,
+ height: 0,
+ distance: 0,
+ cellWidth: 0,
+ cellHeight: 0,
+ offsetX: new Animated.Value(0),
+ currentIndex: props.initialIndex
+ };
+ }
+
+ componentWillMount() {
+ this._panResponder = PanResponder.create({
+ onStartShouldSetPanResponder: (evt, gestureState) => true,
+ onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
+ onMoveShouldSetPanResponder: (evt, gestureState) => true,
+ onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
+ onPanResponderTerminationRequest: (evt, gestureState) => true,
+ onPanResponderTerminate: (evt, gestureState) => {
+ },
+ onShouldBlockNativeResponder: (evt, gestureState) => true,
+ onPanResponderMove: Animated.event([null, {
+ dx: this.state.offsetX
+ }]),
+ onPanResponderGrant: (evt, gestureState) => {
+ this.state.offsetX.setOffset(this.state.offsetX._value);
+ this.state.offsetX.setValue(0);
+ },
+ onPanResponderRelease: (evt, gestureState) => {
+ this.state.offsetX.flattenOffset();
+
+ let {vx, dx} = gestureState;
+ let _distance = this._getDistance(vx, dx);
+
+ Animated.timing(this.state.offsetX, {
+ toValue: _distance,
+ duration: DURATION
+ }).start(this.handleAnimationStop(_distance));
+ }
+
+ });
+ }
+
+ handleAnimationStop(distance) {
+ this.setState({
+ distance: distance
+ })
+ }
+
+ handleContainerLayout(event) {
+ let {nativeEvent: {layout: {x, y, width, height}}} = event;
+ this.setState({
+ width: width,
+ height: height
+ })
+ }
+
+ handleCellLayout(event) {
+ let {nativeEvent: {layout: {x, y, width, height}}} = event;
+ let offsetX = (this.state.width - this.props.space - this.state.cellWidth) / 2 - (this.state.cellWidth + this.props.space) * (this.props.initialIndex - 1);
+
+ this.setState({
+ cellWidth: width,
+ distance: offsetX,
+ cellHeight: height
+ });
+ this.state.offsetX.setValue(offsetX);
+ }
+
+ renderCell(cellData, props) {
+ let cell = this.props.renderCell(cellData);
+ return React.cloneElement(cell, props);
+ }
+
+ render() {
+
+ let {offsetX} = this.state;
+
+ let {space, data, style, contentStyle} = this.props;
+
+ let [translateX] = [offsetX];
+
+ let parallaxViewStyle = {
+ paddingLeft: space / 2,
+ paddingRight: space / 2,
+ width: this.state.cellWidth + space
+ };
+
+ let animatedStyle = {transform: [{translateX}]};
+
+ let content = (
+ data.map((section, i) => {
+ return (
+
+ {this.renderCell(section, {
+ ref: ( (ref) => this._cell = ref),
+ onLayout: (this.handleCellLayout.bind(this))
+ }
+ )}
+
+ )
+ })
+ );
+
+ return (
+ this._animatedView = ref}
+ onLayout={this.handleContainerLayout.bind(this)}
+ style={{...style,flexDirection:'row'}}
+ >
+ {content}
+
+ );
+ }
+
+ _getDistance(vx, dx) {
+
+ let {data, space} = this.props;
+
+ let {distance, currentIndex, cellWidth} = this.state;
+
+ let _cellWidth = cellWidth + space;
+
+ let coefficient = 0;
+ let _distance = vx * DURATION;
+ let direction = vx > 0 ? 1 : -1;
+
+ if (vx > -0.1 && vx < 0.1) {
+ coefficient = Math.round(dx / (_cellWidth));
+ } else if ((vx > -2 && vx < -0.1 ) || (vx > 0.1 && vx < 2)) {
+ coefficient = Math.ceil(Math.abs(_distance) / _cellWidth) * direction;
+ } else if ((vx > -3 && vx < -2 ) || (vx > 2 && vx < 3)) {
+ coefficient = Math.floor(Math.abs(_distance) / _cellWidth) * direction;
+ } else {
+ coefficient = Math.floor(Math.abs(_distance) * 0.8 / _cellWidth) * direction;
+ }
+
+ while (currentIndex - coefficient < 1) {
+ coefficient = coefficient - 1;
+ }
+
+ while (currentIndex - coefficient > data.length) {
+ coefficient = coefficient + 1;
+ }
+
+ this.setState({
+ currentIndex: currentIndex - coefficient
+ });
+
+ _distance = distance + _cellWidth * coefficient;
+ return _distance;
+ }
+}
+
+CenterContentView.PropTypes = {
+ data: PropTypes.array,
+ space: PropTypes.number,
+ renderCell: PropTypes.func,
+ initialIndex: PropTypes.number,
+ style: PropTypes.oneOfType([
+ PropTypes.object,
+ PropTypes.number
+ ]),
+ contentStyle: PropTypes.oneOfType([
+ PropTypes.object,
+ PropTypes.number
+ ])
+};
+
+CenterContentView.defaultProps = {
+ initialIndex: 3,
+ space: CELL_SPACE
+};
+
+