react-native-snap-carousel
react-native-snap-carousel copied to clipboard
Multiple active items in each screen
Would it be possible to have multiple items at one slide just like Apple Music:
![screen shot 2017-07-26 at 11 38 35](https://user-images.githubusercontent.com/4533204/28614753-294e0e00-71f7-11e7-9fa0-d69b60274e0b.png)
Would the right approach be to change data such that all elements are grouped with two elements in each group and then print the two elements side-by-side when rendering each item?
However, Airbnb has the same feature where no items are active, but you can only scroll such that two elements are always visible. Can the same behaviour be achieved with the right size options with react-native-snap-carousel
?
![screen shot 2017-07-26 at 12 47 39](https://user-images.githubusercontent.com/4533204/28617498-cc717ece-7200-11e7-942e-b33c350b7356.png)
Regarding your first question, having multiple acting items at once is possible but would require some heavy refactoring. I don't have time to work on it at the moment, but I'll keep that in mind for future releases.
And yes, as a workaround you can group two elements in one slide to emulate the behavior, if:
- the special index handling is not a no-go for you
- you're ok with snapping two slides every time you move.
Otherwise, I confirm that you can easily reproduce the second layout. Here is a quick example, using the FlatList
version (for the activeSlideAlignment
prop):
const { width: viewportWidth, height: viewportHeight } = Dimensions.get('window');
const SLIDE_WIDTH = Math.round(viewportWidth / 2.6);
const ITEM_HORIZONTAL_MARGIN = 15;
const ITEM_WIDTH = SLIDE_WIDTH + ITEM_HORIZONTAL_MARGIN * 2;
const SLIDER_WIDTH = viewportWidth;
<Carousel
sliderWidth={SLIDER_WIDTH}
itemWidth={ITEM_WIDTH}
activeSlideAlignment={'start'}
inactiveSlideScale={1}
inactiveSlideOpacity={1}
/>
Awesome! Thanks! Great library :-D
However, it seems if I have uneven number of items, I can swipe to a non-existing item such that, if I scroll as much as I can to the right, the last item is in the left of the screen and a non-existing item takes up the right part of the screen.
If you're talking about your first idea (2 slides in one), that's to be expected since your itemWidth
value is twice the real item's width.
Don't forget that you're "hacking" the plugin ;-)
is there any known reason why on my app all items are look active even I set inactiveSlide values? Like this
<Carousel
data={this.props.items.toArray()}
renderItem={this.renderItem}
contentContainerCustomStyle={{ alignItems: 'center' }}
inactiveSlideScale={0.8}
inactiveSlideOpacity={0.7}
sliderWidth={this.state.sliderWidth}
itemWidth={this.state.itemWidth}
enableSnap={true}
loop={true}
lockScrollWhileSnapping={true}
autoplay={true}
autoplayDelay={500}
autoplayInterval={3000}
/>
@markortiz What is your React Native version? If it is not one of the latest, see #173 and particularly this comment.
@bd-arc i'm currently using "react-native": "0.44.0",
base on your explanation. I see the issue now.
But on 3.1.0
infinite loop is not yet supported right?
Hi @markortiz,
I didn't realize you've edited your message. Yes, you're right: the loop feature has been released in version 3.3.0
.
You have a choice to make:
- stay with
3.1.0
to get item's animation - update to
3.3.0+
to get the loop feature - upgrade your RN version to get both.
No easy path here, sorry.
@bd-arc How I can solve this problem?
However, it seems if I have uneven number of items, I can swipe to a non-existing item such that, if I scroll as much as I can to the right, the last item is in the left of the screen and a non-existing item takes up the right part of the screen.
Hi @donnes,
I'm going to need more info to help you with that. Can you please open a new issue and make sure to follow the contributing guidelines?
Hi @bd-arc I think is not necessary create a new issue, because the problem is related to this issue.
I create a reproduce Snack to be easy to find the problem, here's the link: https://snack.expo.io/ryxxwAeJG
As @mazing said earlier, following your example (https://github.com/archriss/react-native-snap-carousel/issues/102#issuecomment-318061156) it's will generate a blank space at end of the scroll. If you run the exemple above and scroll to right you'll can see a blank space.
I don't know how solve this problem, I tried many things but can't find a solution.
@donnes I see what you mean. I didn't provide the full example at that time and I now realize that the code from my previous answer can lead to issues like yours.
I've modified the Snack you provided to make it work as expected: https://snack.expo.io/Hynr0n-1z
They were two problems:
-
sliderItemHorizontalMargin
needed to be added aspaddingHorizontal
instead ofmarginHorizontal
since the latter would modify item's length and completely mess with the plugin (this explains the fact that the view was scrolling a bit more to the right each snap, and ended up with a large "blank" section). - an outer (yellow) and inner (purple) containers were needed in order for the margin to be properly applied.
Note that plugin's official example implements those two suggestions.
I'll update the relevant doc section to the following:
const horizontalMargin = 20;
const slideWidth = 280;
const sliderWidth = Dimensions.get('window').width;
const itemWidth = slideWidth + horizontalMargin * 2;
const itemHeight = 200;
const styles = Stylesheet.create({
slide: {
width: itemWidth,
height: itemHeight,
paddingHorizontal: horizontalMargin
// other styles for your item's container
},
slideInnerContainer: {
width: slideWidth,
flex: 1
// other styles for the visible container
}
};
Do you think it will make things clear enough?
@bd-arc I believe I did not explain the issue I was passing correctly.
Below is an example of the Carousel on the Airbnb app
And now the example I sent earlier (https://snack.expo.io/BJ4_VmS1M)
Looking at the examples that I sent you can notice that in the second gif, when the scroll comes to the end, there is a red space as if there was a "ghost" Card.
I believe that the correct behavior is when the scroll reaches the end, the scroll does not exceed the last Card. Some like this:
@donnes Ah, I now see what you mean!
To be honest, this can prove tough to implement because it would require modifying ScrollView
's width and a bunch of the associated logic when activeSlideAlignment
is set to either start
or end
.
But I'll make sure to take a look at it, as I agree that this would be better from a user experience point of view ;-)
@bd-arc I'll create a fork and try take a look about it. Thank you for the good job! :D
@donnes I've just played with the source code to see what I would be able to do about it, but I unfortunately don't think that it can be implemented at the moment.
Currently, we determine the active item when its position meets an imaginary line linked to the value of activeSlideAlignment
. For example, with a start
value this line is at itemWidth / 2
. If we're not able to scroll to this position, the last items will never be seen as active, impairing the entire plugin logic.
The VirtualizedList
component provides a scrollToIndex()
method that accepts two interesting options: viewPosition
and viewOffset
. I've previously tried using those as they were supposed to allow the kind of behavior you're after. Unfortunately, they have major drawbacks:
-
viewPosition
is buggy as hell -
viewOffset
doesn't work on Android -
scrollToIndex()
is a no-go for now (see this React Native issue).
If you have any idea about how to implement it, I'll be glad to hear your thoughts. Because it seems that I'm kind of stuck until Facebook improves the underlying components (by the way, you can vote to let their team know about it ;-)).
@bd-arc I had previously tried implemented myself carousel component using FlatList and I got these problems too. Aparently the only way is use ScrollView (for IOs) and ViewPagerAndroid (for Android) but yet I got some problems with the latest versions of React Native (0.50+).
By the way, I'll stud the repo code and try some solutions and I back if find something. And also, I'm going to vote for these features as you are suggested.
Again, thank you for the good job! 👍
@donnes While the plugin is based on <FlatList />
, it is nothing more than a <ScrollView />
in the end ;-)
The ViewPagerAndroid
component has been heavily considered since it behaves so much better than the regular ScrollView
on Android. Unfortunately, the preview effect (which is the root of the plugin) can't be implemented with it at the moment. A prop peekEnabled
has recently been added to it, and our expectations were high, but it just doesn't work...
As you can see, I've tried a lot of things to improve the plugin, but RN's core always gets in the way. I'll be pretty glad if you manage to find a solution :-)
You can style Carousel with prop contentContainerCustomStyle
contentContainerCustomStyle={{overflow: 'hidden', width: widthItemSlide * (numberSlide)}}
Everything will be Okay
Yeah, it's a huge miss in my opinion too. Amazing module but in this situation (multi elements) the space totally kills the user interface.
@univers3 I heartily agree with you and I'd really like to be able to implement it this way.
But if you've read this comment, you already know that this is no piece of cake and that the horde of React Native bugs add another layer of complexity...
@phithu Amazing! Although it's not an implementation in the module but instead just limiting the width, it works perfectly in my case.
For reference, this is what I did:
<Carousel
data={this.props.data}
renderItem={this._renderItem}
itemWidth={100}
activeSlideAlignment={'start'}
{/** Other props here... */}
contentContainerCustomStyle={{
overflow: 'hidden',
width: 100 * this.props.data.length
}}
/>
thank you @FoxInFlame @phithu! Wanted to note that on Android 9 devices when only SINGLE item Carousel is broken by setting width
in contentContainerCustomStyle
leading to empty Carousel. So I don't set width in this case
const NUMBER_WHICH_CAUSES_BUG_ON_ANDROID_9 = 1;
contentContainerCustomStyle={{
overflow: 'hidden',
width:
// workaround for case when 1 item
connectedItems.length === NUMBER_WHICH_CAUSES_BUG_ON_ANDROID_9
? undefined
: this.itemWidth * connectedItems.length,
}}
@bd-arc How I can solve this problem?
However, it seems if I have uneven number of items, I can swipe to a non-existing item such that, if I scroll as much as I can to the right, the last item is in the left of the screen and a non-existing item takes up the right part of the screen.
I'm just using snap-carousel recently and come across this issue as well for the 'right empty space'. I got the solution but require to add some additional code in Carousel.js
First we need to add an additional prop lastItemOffsetWidth: PropTypes.number
to know the remaining width on the right we want e.g. margin 10 or 20.
lastly, in function:
_initPositionsAndInterpolators (props = this.props) {
const { data, itemWidth, itemHeight, scrollInterpolator, vertical, activeSlideAlignment, loop, sliderWidth, lastItemOffsetWidth } = props;
...
this._getCustomData(props).forEach((itemData, index) => {
...
if(this._getCustomData(props).length-1 == index && activeSlideAlignment=='start' && !loop){
var offset=lastItemOffsetWidth;
if(offset==undefined) offset=0;
this._positions[index] = {
start: index * sizeRef - (sliderWidth - (sizeRef + offset)),
end: index * sizeRef - (sliderWidth - (sizeRef + offset)) + sizeRef
};
}else{
this._positions[index] = {
start: index * sizeRef,
end: index * sizeRef + sizeRef
};
}
...
should do the trick.
Hi @t-vc,
Thanks for sharing your idea!
Will it work with all values of activeSlideAlignment
(start
, center
, end
)? If yes, do you mind creating a PR for that and share a quick video of your code in action?
Do you have any update on this? I was looking for a solution also
Hi, sorry i didn't realise a reply until @kristoff2016 recent comment.
@bd-arc I focus only for activeSlideAlignment start
to avoid crashing the others.
I will share it to you guys after 3rd December as currently I can't access my files until date.
@t-vc I hope you can share the solution how can you remove the right white space. thanks in advance :)
if(this._getCustomData(props).length-1 == index && activeSlideAlignment=='start' && !loop){
var offset=lastItemOffsetWidth;
if(offset==undefined) offset=0;
this._positions[index] = {
start: index * sizeRef - (sliderWidth - (sizeRef + offset)),
end: index * sizeRef - (sliderWidth - (sizeRef + offset)) + sizeRef
};
}else{
this._positions[index] = {
start: index * sizeRef,
end: index * sizeRef + sizeRef
};
}
if u notice the original code, this._position[index] is actually calculating each item (start from first to last) snap point, i just add an IF statement to change the last item's snap point, that's all.
thanks for sharing which file change that I should change or do it? or go to change in the original file in this library? could you help advice me? thank you in advance
just modify the original file Carousel.js