React-native-deck-swiper: Flickering with image content

Created on 22 May 2017  路  31Comments  路  Source: alexbrillant/react-native-deck-swiper

bug help wanted

Most helpful comment

Hello i just add unique key from card on view (renderFirstCard, renderSecondCard, renderSwipeBackCard).

Swiper.js :
```
renderFirstCard = () => {
const { firstCardIndex } = this.state
const { cards } = this.props

const swipableCardStyle = this.calculateSwipableCardStyle()
const firstCardContent = cards[firstCardIndex]
const firstCard = this.props.renderCard(firstCardContent)
const renderOverlayLabel = this.renderOverlayLabel()

const notInfinite = !this.props.infinite
if (notInfinite && this.state.swipedAllCards) {
  return <Animated.View />
}

return (
  <Animated.View
    style={swipableCardStyle}
    key={firstCardContent.product__id}
    {...this._panResponder.panHandlers}
  >
    {renderOverlayLabel}
    {firstCard}
  </Animated.View>
)

}

renderSecondCard = () => {
const { secondCardIndex } = this.state
const { cards, renderCard, showSecondCard } = this.props

const secondCardZoomStyle = this.calculateSecondCardZoomStyle()
const secondCardContent = cards[secondCardIndex]
const secondCard = renderCard(secondCardContent)

const notInfinite = !this.props.infinite
const lastCardOrSwipedAllCards =
  secondCardIndex === 0 || this.state.swipedAllCards
if (notInfinite && lastCardOrSwipedAllCards) {
  return <Animated.View />
}

return (
  <Animated.View key={secondCardContent.product__id} style={secondCardZoomStyle}>
    {showSecondCard ? secondCard : null}
  </Animated.View>
)

}

renderSwipeBackCard = () => {
const { previousCardIndex } = this.state
const { cards } = this.props
const previousCardContent = cards[previousCardIndex]
const previousCardStyle = this.calculateSwipeBackCardStyle()
const previousCard = this.props.renderCard(previousCardContent)
return (
{previousCard}

)
}

```
Work for you ?

All 31 comments

I have the same problem here.. It flickers after the swiping animation ended

+1 same problem here
It is not a fundamental solution, but I have solved it by using react-native-fast-image.
Hope it works!

Same here!

@mu29 Thanks for the work around

@mu29 works for me aswell. Awesome.

@mu29 Do you known why react-native-fast-image solve it for you? I'm trying to understand what caused the flickering, RN's Image's problem or react-native-deck-swiper's.

@mu29 I added your workaround to the README thanks alot ! Do you think I should close this issue or leave it opened ?

@alexbrillant I think we shoud keep this issue opened :)

@mu29 alright I'll keep it opened. It is also related to the fact that I reuse the card Views...

@lsps9150414 alexbrillant's comment may help you

Hello i just add unique key from card on view (renderFirstCard, renderSecondCard, renderSwipeBackCard).

Swiper.js :
```
renderFirstCard = () => {
const { firstCardIndex } = this.state
const { cards } = this.props

const swipableCardStyle = this.calculateSwipableCardStyle()
const firstCardContent = cards[firstCardIndex]
const firstCard = this.props.renderCard(firstCardContent)
const renderOverlayLabel = this.renderOverlayLabel()

const notInfinite = !this.props.infinite
if (notInfinite && this.state.swipedAllCards) {
  return <Animated.View />
}

return (
  <Animated.View
    style={swipableCardStyle}
    key={firstCardContent.product__id}
    {...this._panResponder.panHandlers}
  >
    {renderOverlayLabel}
    {firstCard}
  </Animated.View>
)

}

renderSecondCard = () => {
const { secondCardIndex } = this.state
const { cards, renderCard, showSecondCard } = this.props

const secondCardZoomStyle = this.calculateSecondCardZoomStyle()
const secondCardContent = cards[secondCardIndex]
const secondCard = renderCard(secondCardContent)

const notInfinite = !this.props.infinite
const lastCardOrSwipedAllCards =
  secondCardIndex === 0 || this.state.swipedAllCards
if (notInfinite && lastCardOrSwipedAllCards) {
  return <Animated.View />
}

return (
  <Animated.View key={secondCardContent.product__id} style={secondCardZoomStyle}>
    {showSecondCard ? secondCard : null}
  </Animated.View>
)

}

renderSwipeBackCard = () => {
const { previousCardIndex } = this.state
const { cards } = this.props
const previousCardContent = cards[previousCardIndex]
const previousCardStyle = this.calculateSwipeBackCardStyle()
const previousCard = this.props.renderCard(previousCardContent)
return (
{previousCard}

)
}

```
Work for you ?

@rmanniez you nailed it ! it works like a charm thanks alot 馃憤 馃挴

adding a unique key prop to the cards did the trick ! I used the firstCardIndex, secondCardIndex, and previousCardIndex for the keys.

fixed in 1.3.7 !

Awesome :)

I'm still seeing the flickering after upgrading to 1.3.7.
Adding the unique key to renderFirstCard and renderSecondCard did solve the problem that the previous image is shown briefly after swipe but now instead of the previous image, a brief blank is shown.
I believe the brief flash is where renderFirstCard is rendering a new card with the next image but the Image is not ready.

9 -18-2017 11-38-36

I'm using Expo on a simulator with debug JS remotely turned off.

@lsps9150414 This would be a different issue. It doesn't flash the previous image like it did before in 1.3.7. It flashes because the new image is fetched twice. The first card resets when the animation is done(second card has the same image before the first card resets).

@alexbrillant I also found that the issue only happens on iOS not Android. So seems this is a performance issue with the Image component on the iOS platform?

@lsps9150414 Maybe, I don't know. Tell me if you find anything. I need to avoid changing the top view when the animation is done, but I don't know how yet.

@rmanniez Yes I did. It happens even with local images.

@alexbrillant Not changing the top view sounds like a good path to fix this. Is it possible to achieve it by interchanging the z-index of renderFirstCard and renderSecondCard?

@alexbrillant I believe, as @rmanniez suggested, it can be fix by adding secondCardIndex as the key to renderSecoundCard so that react knows to reuse the card component instead of creating a new one: https://github.com/alexbrillant/react-native-deck-swiper/blob/master/Swiper.js#L696

I see you only add the key to renderFirstCard.

@lsps9150414 i found the problem :

Try to remove {renderOverlayLabel} on renderFirstCard(), i don't use overlay and work for me.

Good for you too ?

@alexbrillant maybe you could conditionally not render the renderOverlayLabel inside the renderFirstCard when there is no overlayLabel?

@alexbrillant
I think this issue should be reopened as it still occurs under different circumstances (including with or without overlay labels). I've been doing some thorough tests within the past hour or so and, although it doesn't happen on each swipe), it still happens to roughly 60% of swipes (I've even tested with decks ranging 100+ cards).

It is clear that this has to do with rendering performance (deck size, image size etc) and also it shouldn't be acceptable to just say thing like just don't use feature X of the package ...

Furthermore, even if you set showSecondCard to false flickering still occurs.

My idea is that flickering occurs due to the swipe animation, under the following steps:

  1. Swipe in any direction is performed
  2. Animation is triggered to move the card out of view
  3. Top card is placed on top of the deck
  4. Content changes (second card -> first card, then new content on second card)

Sometimes it doesn't flicker because steps 3 and 4 happen so quickly that we cannot actually see that with the human eye (this happens when processing is blazing fast).
Sometimes it flickers because processing isn't fast enough and there's some lag between steps 3 and 4.

One solution would be to set opacity of 1st card to 0 when animation finishes, move it back on top, load content, then set opacity back to 1. This way, should processing be slow, we will not be able to see any flickering.

@jalieven You have to try to include in the renderCard I think. I will try when I have a time.

Further testing revealed the issue. Sometimes, while swiping (not at 1st swipe) the following happens:

  1. First card (1) rendered; Second card (2) rendered beneath;
  2. Swipe happens
  3. First card (1) rendered; Second card (OLD-ID; from an older swipe) is rendered;
  4. Second card (2) is rendered at First card; Second card (OLD-ID; from an older swipe) is rendered as second card;
  5. Second card (2) is rendered at First card; New card (NEW) is rendered as second card.

(X) denotes card unique IDs based on card properties
Steps 3, 4 and 5 happen blazingly fast.

Also done some video recordings and can confirm that a brief blank image area is displayed. Furthermore, if you make all parent elements background transparent (including the image itself), the flash is then triggered by the main screen content.

The attached screenshot is a frame right when the flash occurs (just after re-rendering of 1st and second card happens after a swipe).

Bottom line, this is definitely cause by images not rendering fast enough and the biggest issue, since renderCard is a function passed to the Swiper, is that it is not very easy to reuse the same image: render secondcard's image in firstcard's image upon swipe without any re-renders of the image element.

Also, the prevalence of this issue is higher and directly proportional to the image size (resolution, internet connect, and device computing resources at the given time of rerender). Naturally, larger images will trigger flickers, while low resolution images may not.

screen shot 2017-10-25 at 16 19 47

If you are using static images, the problem can be solved by using the defaultSource prop from Image component, as it is suggested here: https://github.com/facebook/react-native/issues/981#issuecomment-354767548

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Shubh1692 picture Shubh1692  路  8Comments

georgeMorales picture georgeMorales  路  4Comments

MindRave picture MindRave  路  9Comments

siquick picture siquick  路  6Comments

deadcoder0904 picture deadcoder0904  路  6Comments