Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The modal emits a "flash" when used with useNativeDriver=true #92

Closed
10 tasks done
Versus2017 opened this issue Dec 11, 2017 · 61 comments
Closed
10 tasks done

The modal emits a "flash" when used with useNativeDriver=true #92

Versus2017 opened this issue Dec 11, 2017 · 61 comments
Labels

Comments

@Versus2017
Copy link

Versus2017 commented Dec 11, 2017

When reporting a bug, please be sure to check if the issue still persists with react-native original modal:

Under the hood react-native-modal uses react-native original Modal component.
Before reporting a bug, try swapping react-native-modal with react-native original Modal component to check if the problem persists.

When reporting a bug, please be sure to include the following:

  • The outcome of the react-native-modal swap described above
  • A descriptive title
  • An isolated way to reproduce the behavior (example: GitHub repository with code isolated to the issue that anyone can clone to observe the problem)
  • What version of react-native-modal you're using, and the platform(s) you're running it on (iOS, Android, device)
  • What packages or other dependencies you're using
  • The behavior you expect to see, and the actual behavior

When you open an issue for a feature request, please add as much detail as possible:

  • A descriptive title
  • A description of the problem you're trying to solve, including why you think this is a problem
  • An overview of the suggested solution
  • If the feature changes current behavior, reasons why your solution is better

Please note by far the quickest way to get a new feature is to file a Pull Request.

@Versus2017
Copy link
Author

Versus2017 commented Dec 11, 2017

I found that react-native-modal didn't clear all the content view when you want to close it. it will apparently show a flash view when you use useNativeDriver, finally I found it related to Modal class of react-native.

Here is the error:

My code just like this:

<Modal
    backdropColor={'black'}
    backdropOpacity={0.5}
    useNativeDriver={true}
  >
    <View style={styles.taskListWrapper}>
      <TaskListPage
        style={styles.taskList}
      />
    </View>
</Modal>

Obviously I used useNativeDriver, and the children view is TaskListPage view, it is a complex view, so react-native need to spend a lot of time to render it, then you may see a flash appeared when you close your modal. as far as I see the more complex of the children view the more frequently you may see the flash after closing.

My analysis:

I've made a lot of test, and finally i found that it related to the Modal of react-native in index.js file. although its animationType is none, but you can still see the disappear of the modal. So it will has a flash, I tried use slide animationType, and it obviously shows more specifics of what i saw, you guys can have a try.

That's what i think , I still try to find a better solution of the problem for now.
Hoping that you guys can give me some useful infomation, Thanks!

@Versus2017 Versus2017 changed the title Didn't clear the content view when closing. the modal content view cache shows a flash when closing. Dec 11, 2017
@mmazzarolo
Copy link
Member

Hey @Versus2017 , thanks for submitting the issue.
It's is still not totally clear to me what kind of "flash" you're experiencing.

I don't know if it can be useful to you but I'm using this modal in some applications with huge lists and heavy renders and my approach is almost always to hide the content of the Modal before setting its visibility to false/true.

Regarding your suggestion: unfortunately currenly using the React-Native built in Modal is the only way for showing your content on top of the screen (without using it at root level).

@Versus2017
Copy link
Author

Versus2017 commented Dec 11, 2017

Yeah, thanks for replying, I just noticed that we can only use Modal to show our content on the top of the screen.
I will have a try about what you said. thanks so much~

@wkoutre
Copy link
Contributor

wkoutre commented Dec 11, 2017

@Versus2017 I have the same problem you’ve described. As you’ve probably found, the flash goes away if the native driver is not used. The content of my modals isn’t very complex, though, so using the JS thread for animation isn’t an issue for me.

With debug builds, using the JS thread makes the animation seem very laggy w/ all the debugging tools running. On a release build, though, it’s very smooth.

@mmerickel
Copy link
Contributor

mmerickel commented Dec 12, 2017

I'm experiencing this as well and can confirm that the issue goes away if I set useNativeDriver={false}.

Basically after onModalHide is invoked there is a quick flash where the modal comes back for a tick and then it is gone again.

@Versus2017
Copy link
Author

Versus2017 commented Dec 12, 2017

@mmerickel yes, you and me have the same problem now, and if I set useNativeDriver={false} , my animation seems not so smooth sometimes. so I need use useNativeDriver.

I just tried what the author said that hide the content of the Modal before setting its visibility to false. It did solved my problem, the quick flash almost disappear (sometimes it still shows, but it better than before), but the bad news is the closing animation is gone. the content just disappeared directly without any animationOut animation

@Versus2017
Copy link
Author

Versus2017 commented Dec 12, 2017

@mmazzarolo I think your idea is the solution. but why can't we do that in your react-native-modal code. I just tried in your code. it's perfectly worked and without any flash again, and the animationOut is still exist.

I just add a state prop showContent just like you said, I need hide the content of the Modal before setting its visibility to false, So I use showContent prop to hide the content of the Modal after closing animation finished in _close function in index.js file. Can we do that ?

here is what I do : https://github.com/Versus2017/react-native-modal

@mmazzarolo
Copy link
Member

Thanks everyone for the discussion!
I wasn't aware of the issue of the useNativeDriver={true}... which is something I'm not totally in control of since it is related to react-native-animatable.

but why can't we do that in your react-native-modal code
We can do it! I simply never thought it could have been an useful feature.
The only issue I see on adding it is that it might lead to further props/customizations that would make the modal even more complex to use for newcomers (e.g.: a props that controls the kind of animation used for showing/hiding the content only).

What's your opinion on the subject guys? @Versus2017 @mmerickel @wkoutre

@Versus2017
Copy link
Author

On the one hand, I think your concern about that is right, it may actually confuses newcomers, and most of users may never meet the problem which happend to me. On the other hand, It is necessary for developers who need more higher performance of animation, and has more complex content with heavy render, just like me. It is big problem for them.

Here is our logic:

  • we need more higher performance for animation, so we need to set useNativeDriver to true.
  • and next, we meet the problem with a quick flash after closing animation finished as I said before.
  • and then, we analyze the problem, we know that it related to Modal of react-native
  • finally, we think we should clear the content of Modal after closing animation finished.

Here is our question :

Is it necessary to add this new feature in react-native-modal ?
I think it is necessay to add this new feature to react-native-modal, cause it will improve its performance of animation and i don't see any side effects in code, but not in this way of what i did in my own code. The improvement should be more clearly and easy to understand and use.

For example:

  • We don't need to expose it to users, and we bind it to useNativeDriver prop inside of the code. So if they use useNativeDriver={true}, we just open it to clear content view of Modal after closing animation finshed.

@RichardLindhout
Copy link
Contributor

May be related to oblador/react-native-animatable#122

@wkoutre
Copy link
Contributor

wkoutre commented Dec 13, 2017

I agree with @Versus2017 when he writes:

We don't need to expose it to users, and we bind it to useNativeDriver prop inside of the code. So if they use useNativeDriver={true}, we just open it to clear content view of Modal after closing animation finshed.

I don't see how it would create any side effects if this is the standard, implicit functionality when using useNativeDrive={true}.

@mmazzarolo
Copy link
Member

I don't see how it would create any side effects if this is the standard, implicit functionality when using useNativeDrive={true}.

The issue I can see is that this solution would hide the content of the modal for the entire duration of the animations. This could lead to nasty visual effects, especially if the user sets an high animation timing.

@Versus2017
Copy link
Author

Versus2017 commented Dec 14, 2017

I think you should have a try with useNativeDriver={true} if you don't exactly know where the problem is. And I take some tests with react-native-modal examples with useNativeDriver={true}, and it hardly to show the quick flash, but when I add some other components to the content to make it a little more complex, it actually shows the quick flash after you tapped the close button.

The problem which I want to fix is:

  1. I set useNativeDriver={true}, because I need more smooth animation for my heavy render content
  2. It will shows a quick flash of content when isVisible changed to false.

The Code is here:

_close = () => {
  if (this.transitionLock) return;
  this.transitionLock = true;
  this.backdropRef.transitionTo({ opacity: 0 }, this.props.backdropTransitionOutTiming);
  this.contentRef[this.animationOut](this.props.animationOutTiming).then(() => {
    this.transitionLock = false;
    if (this.props.isVisible) {
      this._open();
    }
    else {

      // It will shows a quick flash when you set isVisible to false, 
      // the quick flash is Modal of react-native disappear flash

      this.setState({ isVisible: false })
      this.props.onModalHide();
    }
  });
};

return (
  <Modal
    transparent={true}
    animationType={'none'}
    visible={this.state.isVisible}
    onRequestClose={onBackButtonPress}
    {...otherProps}
  >

I guess the reason why we set useNativeDriver to true and the Modal will shows a quick flash when visible is false is useNativeDriver has some effects on Modal. So it will shows the quick animation of Modal disappear though the animationType is 'none', finally it becomes the quick flash of what you saw.

@wkoutre
Copy link
Contributor

wkoutre commented Dec 14, 2017

What happens when changing:

this.setState({ isVisible: false })
this.props.onModalHide();

to

this.setState({ isVisible: false }, this.props.onModalHide)

I guess my question here is: Are you certain the flash happens when isVisible is set to false? I think I remember coming to the conclusion that the flash actually happens when onModalHide is called — when I was first testing, it always occurred right after the amount of ms I set the hiding animation to be.

@RichardLindhout
Copy link
Contributor

RichardLindhout commented Dec 14, 2017

The flash does not always happen, but here is an illustration of the problem. I think it's probably a react-native problem with Animation.

dec -14-2017 20-49-25

@Versus2017
Copy link
Author

@RichardLindhout I test it with a high animation timing, I found it happen when animation finished, so I don't think that it is react-native problem. you also can have a try, the problem you can see is modal disappear without animation

@RichardLindhout
Copy link
Contributor

Hmm.. I still think it's a react-native problem since it's a difference in result when using the nativeDriver.
It's just a prop to the Animated object in React Native.

https://facebook.github.io/react-native/docs/animated.html

But there's no doubt that there will be a work-around to fix this nasty effect. If I have some spare time I'll dig in to it.

@RichardLindhout
Copy link
Contributor

Interesting issue: facebook/react-native#14219

@RichardLindhout
Copy link
Contributor

Same issue: oblador/react-native-animatable#148

Seems to be a problem which can be tracked down to react-native-animatable and probably react-native.

@wkoutre
Copy link
Contributor

wkoutre commented Dec 16, 2017

@RichardLindhout Have you tried adding this.props.onModalHide to the callback of setting state's isVisible: false, replacing the current synchronous implementation?

@RichardLindhout
Copy link
Contributor

RichardLindhout commented Dec 18, 2017

I could not reproduce this flash bug when making a simple modal with react-native and the native driver.

import React, { Component } from 'react'

import {
  View,
  Dimensions,
  Platform,
  // StatusBar,
  Text,
  Linking,
  Animated,
  TouchableOpacity,
  Easing,
  TouchableWithoutFeedback,
} from 'react-native'

import Gradient from './Gradient'

class Start extends Component {
  constructor(props) {
    super(props)

    this.state = {
      hide: false,
    }
  }
  componentWillMount() {
    this._visibility = new Animated.Value(this.state.hide ? 1 : 0)
  }

  _toggle = () => {
    const animation = Animated.timing(this._visibility, {
      toValue: this.state.hide ? 0 : 1,
      duration: 300,
      useNativeDriver: true,
      ease: Easing.bezier(0.25, 0.1, 0.25, 1),
    })

    if (this.state.hide) {
      this.setState(
        {
          hide: !this.state.hide,
        },
        () => animation.start()
      )
    } else {
      animation.start(() =>
        this.setState({
          hide: !this.state.hide,
        })
      )
    }
  }
  render() {
    const translateY = this._visibility.interpolate({
      inputRange: [0, 1],
      outputRange: [0, -Dimensions.get('window').height],
    })

    const containerStyle = {
      flex: 1,

      alignItems: 'center',
      justifyContent: 'center',

      transform: [
        {
          translateY,
        },
      ],
    }

    return (
      <View style={{ backgroundColor: 'blue', flex: 1 }}>
        <TouchableOpacity
          style={{ backgroundColor: 'red', width: 120, height: 80 }}
          onPress={() => alert('content touchable working')}
        />
        <View
          style={{
            //backdrop
            backgroundColor: this.state.hide
              ? 'rgba(0,0,0,0)'
              : 'rgba(0,0,0,0.5)',
            position: 'absolute',
            top: 0,
            bottom: 0,
            left: 0,
            right: 0,
            width: null,
            height: null,
          }}
          pointerEvents={this.state.hide ? 'box-none' : 'auto'}
        >
          <Animated.View style={containerStyle} pointerEvents="box-none">
            {!this.state.hide && (
              <View
                style={{
                  backgroundColor: 'white',
                  flexDirection: 'column',

                  borderRadius: 70,
                  height: 400,
                  width: 300,
                }}
              >
                <View
                  style={{
                    backgroundColor: 'green',
                    width: 20,
                    height: 40,
                  }}
                />

                <TouchableOpacity
                  style={{ backgroundColor: 'pink', width: 120, height: 80 }}
                  onPress={() => alert('touchable working')}
                />

                <View
                  style={{
                    backgroundColor: 'green',
                    width: 20,
                    height: 40,
                    flex: 1,
                  }}
                />
              </View>
            )}
          </Animated.View>
        </View>

        <TouchableOpacity
          onPress={this._toggle}
          style={{
            position: 'absolute',
            bottom: 0,
            right: 0,
            left: 0,
            width: null,
            backgroundColor: '#fff',
            padding: 10,
          }}
        >
          <Text>Test modal hide/show</Text>
        </TouchableOpacity>
      </View>
    )
  }
}

export default Start


@Versus2017
Copy link
Author

@RichardLindhout I agree with you about that maybe it is react-native animated problem. but I have fix this issue in my own code, I removed the content after animation, so I think it is not the main problem about react-native Animated animation. here is my code https://github.com/Versus2017/react-native-modal. There will be no more flash again.

@RichardLindhout
Copy link
Contributor

@Versus2017 Thanks for your repository. We wil probably switch branches.

@mmazzarolo
Copy link
Member

@Versus2017 @RichardLindhout @wkoutre sorry guys but I've been super busy lately, I hope to take a closer inspection at the issue during my xmas holiday.
In the meanwhile, thanks for your contributions :)

@RichardLindhout
Copy link
Contributor

@Versus2017 I keep getting the same flash with your repository

@Versus2017
Copy link
Author

Versus2017 commented Dec 22, 2017

@RichardLindhout I don't get the flash any more with my repository for now, maybe I am still not solve the problem, and maybe it's actually related to React-Native animation, I will continue to follow this problem. Thanks for your feedback.

@RichardLindhout
Copy link
Contributor

@Versus2017 You're right, I used https://github.com/mmazzarolo/react-native-modal-datetime-picker and this library has another dependency so I'm probably going to fork that one ;)

@RichardLindhout
Copy link
Contributor

@Versus2017 Done: https://github.com/profects/react-native-modal-datetime-picker.

Works like a charm! Thanks ;)

@mmazzarolo
Copy link
Member

Dumb question probably : How can we test it if it is not on npm ?

Just install it directly from the master branch npm i -S git@github.com:react-native-community/react-native-modal.git

Also, What about using a beta tag ?

Sure! I'll do it this evening (I'm at work right now)

@mmazzarolo
Copy link
Member

mmazzarolo commented Feb 22, 2018

Released as beta: npm i -S react-native-modal@5.1.0-0.
Feedbacks are warmly welcomes 🎈

@garan82
Copy link

garan82 commented Feb 23, 2018

I confirm that #116 fixes the flash isssue on iOS for me.

@LouisJS
Copy link
Contributor

LouisJS commented Feb 23, 2018

Unfortunately, i still have the flash issue on iOS 😞

@wellyshen
Copy link

wellyshen commented Feb 23, 2018

Unfortunately, i still have the flash issue on iOS too. But I found the frequency of flash is decreased.

@mmazzarolo
Copy link
Member

Did you set hideModalContentWhileAnimating?

@wellyshen
Copy link

wellyshen commented Feb 23, 2018

@mmazzarolo I think nope. Should I set it?

@mmazzarolo
Copy link
Member

yep!

@LouisJS
Copy link
Contributor

LouisJS commented Feb 23, 2018

It does work now with hideModalContentWhileAnimating
My bad. Awesome 💯

@wellyshen
Copy link

wellyshen commented Feb 23, 2018

@mmazzarolo May I ask how to use it? It doesn't listed on document. Do I just add hideModalContentWhileAnimating with useNativeDriver for the Modal component right?

@mmazzarolo
Copy link
Member

@wellyshen sure!
Setting the new hideModalContentWhileAnimating prop of the modal to true should be enough!

I'm adding it to the README.md too 👍

Let me know if it works for you.

@mmazzarolo
Copy link
Member

I also updated the README by adding a FAQ and the prop description.

@wellyshen
Copy link

@mmazzarolo Thanks man, It works.

@mmazzarolo
Copy link
Member

Nice!
Closed, feel free to open another issue if needed

@gianpaj
Copy link

gianpaj commented Nov 22, 2018

If anybody is experiencing similar flicking issue on Android adding the renderToHardwareTextureAndroid prop helped me.
hideModalContentWhileAnimating or useNativeDriver didn't - FYI I'm using react-native-dialog.

Issue Fixed
<Dialog.Container
    visible={this.state.dialogVisible}
    onBackButtonPress={this.toggleDialog}
    renderToHardwareTextureAndroid>
    <Dialog.Title>title</Dialog.Title>
    <Text>blah</Text>
    <Dialog.Button label="OK" onPress={this.toggleDialog} />
</Dialog.Container>

@anikhechoyantumo
Copy link

Don't know what i am doing wrong, but the flickering exists when using "slideInDown" opening and "slideOutUp" closing animations. Here are my props

<Modal swipeDirection="up" animationIn="slideInDown" animationOut="slideOutUp" isVisible={this.state.visible} backdropTransitionOutTiming={0} swipeThreshold={50} backdropOpacity={0.2} supportedOrientations={['portrait', 'landscape']} onSwipeComplete={this.swipeCompletedHandler} onSwipeStart={this.swipeStartedHandler} onSwipeCancel={this.swipeCancelHandler} hideModalContentWhileAnimating={true} useNativeDriver={true} >

Using useNativeDriver={false} also didn't help.

@alexdieudonne
Copy link

Hello there i've used a trick to "fix" this problem, you just have to pass the property backdropTransitionOutTiming to 0 like the following

            <Modal
                isVisible={modalInput.isModalVisible}
                avoidKeyboard
                useNativeDriver={Platform.OS === "android"}
                hideModalContentWhileAnimating
                backdropTransitionOutTiming={0}  // <- the trick
                backdropOpacity={1}
                animationIn="slideInUp"
                animationOut="slideOutDown"
                />

@sntg-p
Copy link

sntg-p commented May 30, 2023

Hello there i've used a trick to "fix" this problem, you just have to pass the property backdropTransitionOutTiming to 0 like the following

            <Modal
                isVisible={modalInput.isModalVisible}
                avoidKeyboard
                useNativeDriver={Platform.OS === "android"}
                hideModalContentWhileAnimating
                backdropTransitionOutTiming={0}  // <- the trick
                backdropOpacity={1}
                animationIn="slideInUp"
                animationOut="slideOutDown"
                />

useNativeDriverForBackdrop={true} is better if you don't want to lose the background fade in and fade out animations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests