From 650e472dd8e5b6ba69d645c3277e768ba12fd185 Mon Sep 17 00:00:00 2001 From: bd-arc Date: Mon, 10 Oct 2016 19:19:47 +0200 Subject: [PATCH 1/2] refactor(index): properly center slides, handle single slide and add optional props --- index.js | 63 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/index.js b/index.js index 9ab231e74..d9f1f10ff 100644 --- a/index.js +++ b/index.js @@ -18,11 +18,6 @@ export default class Carousel extends Component { * Width in pixels of your elements */ itemWidth: PropTypes.number.isRequired, - /** - * Width in pixels of the horizontal margin - * between your elements - */ - itemHorizontalMargin: PropTypes.number.isRequired, /** * Function returning a react element. The entry * data is the 1st parameter, its index is the 2nd @@ -32,6 +27,14 @@ export default class Carousel extends Component { * Style of each item's container */ slideStyle: PropTypes.number.isRequired, + /** + * Global wrapper's style + */ + containerCustomStyle: PropTypes.number, + /** + * Content container's style + */ + contentContainerCustomStyle: PropTypes.number, /** * Delta x when swiping to trigger the snap */ @@ -46,6 +49,14 @@ export default class Carousel extends Component { * default ones. Can be used w/ animationFunc */ animationOptions: PropTypes.object, + /** + * Scale factor of the inactive slides + */ + inactiveSlideScale: PropTypes.number, + /** + * Opacity value of the inactive slides + */ + inactiveSlideOpacity: PropTypes.number, /** * Index of the first item to display */ @@ -87,7 +98,11 @@ export default class Carousel extends Component { animationOptions: { easing: Easing.elastic(1) }, - slideStyle: {} + slideStyle: {}, + containerCustomStyle: null, + contentContainerCustomStyle: null, + inactiveSlideScale: 0.9, + inactiveSlideOpacity: 1 } constructor (props) { @@ -132,11 +147,11 @@ export default class Carousel extends Component { } _calcCardPositions (props = this.props) { - const { items, itemWidth, itemHorizontalMargin } = props; + const { items, sliderWidth, itemWidth } = props; items.forEach((item, index) => { this._positions[index] = { - start: (itemHorizontalMargin * (index + index === 0 ? 1 : 0)) + (index * itemWidth) + start: index * itemWidth }; this._positions[index].end = this._positions[index].start + itemWidth; }); @@ -163,9 +178,10 @@ export default class Carousel extends Component { } _getCenterX (event) { - const { sliderWidth } = this.props; + const { sliderWidth, itemWidth } = this.props; + const containerSideMargin = (sliderWidth - itemWidth) / 2; - return event.nativeEvent.contentOffset.x + sliderWidth / 2; + return event.nativeEvent.contentOffset.x + sliderWidth / 2 - containerSideMargin; } _onScroll (event) { @@ -248,7 +264,7 @@ export default class Carousel extends Component { } get items () { - const { items, renderItem, slideStyle } = this.props; + const { items, renderItem, slideStyle, inactiveSlideScale, inactiveSlideOpacity } = this.props; if (!this.state.interpolators || !this.state.interpolators.length) { return false; } @@ -263,12 +279,12 @@ export default class Carousel extends Component { {transform: [{ scale: animatedValue.interpolate({ inputRange: [0, 1], - outputRange: [0.85, 1] + outputRange: [inactiveSlideScale, 1] }) }], opacity: animatedValue.interpolate({ inputRange: [0, 1], - outputRange: [0.7, 1] + outputRange: [inactiveSlideOpacity, 1] }) } ]}> @@ -301,27 +317,44 @@ export default class Carousel extends Component { snapToItem (index, animated = true) { const itemsLength = this._positions.length; + if (index >= itemsLength) { index = itemsLength - 1; } else if (index < 0) { index = 0; } - const snapX = this._positions[index].start - ((this.props.sliderWidth - this.props.itemWidth - (2 * this.props.itemHorizontalMargin)) / 2); + const snapX = this._positions[index].start; this.refs.scrollview.scrollTo({x: snapX, y: 0, animated}); } render () { + const { sliderWidth, itemWidth, containerCustomStyle, contentContainerCustomStyle } = this.props; + + const containerSideMargin = (sliderWidth - itemWidth) / 2; + const style = [ + { paddingHorizontal: Platform.OS === 'ios' ? containerSideMargin : 0 }, + containerCustomStyle || {} + ]; + const contentContainerStyle = [ + { paddingHorizontal: Platform.OS === 'android' ? containerSideMargin : 0 }, + contentContainerCustomStyle || {} + ]; + return ( + onScroll={this._onScroll} + scrollEventThrottle={50} + > { this.items } ); From 85982403d3accc707d5dfba4835fed7bea5f6a6a Mon Sep 17 00:00:00 2001 From: bd-arc Date: Mon, 10 Oct 2016 19:41:04 +0200 Subject: [PATCH 2/2] chore(README): add new props' references as well as a few tips --- README.md | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index a0996d8a8..5fdc91eb1 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Simple carousel component with snapping effect on Android & iOS for React Native $ npm install --save react-native-snap-carousel ``` -``` +```javascript import Carousel from 'react-native-snap-carousel'; _renderItem (data, index) { @@ -26,7 +26,6 @@ import Carousel from 'react-native-snap-carousel'; renderItem={this._renderItem} sliderWidth={sliderWidth} itemWidth={itemWidth} - itemHorizontalMargin={itemHorizontalMargin} slideStyle={styles.slide} /> } ``` @@ -40,7 +39,6 @@ Prop | Description | Type | Default items | Array of items to loop on | Array | Required sliderWidth | The width in pixels of your slider | Number | Required itemWidth | Width in pixels of your items | Number | Required -itemHorizontalMargin | Width in pixels of the horizontal margin between your elements | Number | Required renderItem | Function returning a react element. The entry data is the 1st parameter, its index is the 2nd | Function | Required slideStyle | Style of each item's container | Number | Required swipeThreshold | Delta x when swiping to trigger the snap | Number | `20` @@ -49,9 +47,13 @@ animationOptions | Animation options to be merged with the default ones. Can be firstItem | Index of the first item to display | Number | `0` autoplay | Trigger autoplay on mount | Boolean | `false` autoplayInterval | Delay in ms until navigating to the next item | `3000` -autoplayDelay | Deay before enabling autoplay on startup & after releasing the touch | Number | `5000` +autoplayDelay | Delay before enabling autoplay on startup & after releasing the touch | Number | `5000` enableSnap | If enabled, releasing the touch will scroll to the center of the nearest/active item | Number | `true` snapOnAndroid | Snapping on android is kinda choppy, especially when swiping quickly so you can disable it | Boolean | `false` +containerCustomStyle | Optional styles for Scrollview's global wrapper | Number | `null` +contentContainerCustomStyle | Optional styles for Scrollview's items container | Number | `null` +inactiveSlideScale | Value of the 'scale' transform applied to inactive slides | Number | `0.9` +inactiveSlideOpacity | Value of the opacity effect applied to inactive slides | Number | `1` ## Methods @@ -59,11 +61,35 @@ snapOnAndroid | Snapping on android is kinda choppy, especially when swiping qui * `stopAutoplay ()` Stop the autoplay manually * `snapToItem (index, animated = true)` Snap to an item manually +## Tips and tricks + +### Margin between slides +If you need some **extra horizontal margin** between slides (besides the one resulting from the scale effect), you should add it as `paddingHorizontal` on the slide container. Make sure to take this into account when calculating item's width. + +```javascript +const slideWidth = 250; +const horizontalMargin = 40; +const itemWidth = slideWidth + horizontalMargin * 2; + +const styles = Stylesheet.create({ + slide: { + width: itemWidth + // other styles for your item's container + } +}; + + + +``` + ## TODO : - [ ] Improve snap on Android -- [ ] Fix centering since it's a little off -- [ ] Handle passing 1 item only - [ ] Handle changing props on-the-fly - [ ] Handle device orientation event - [ ] Add Vertical implementation +- [x] Fix centering since it's a little off +- [x] Handle passing 1 item only