Giter VIP home page Giter VIP logo

Comments (10)

brentvatne avatar brentvatne commented on June 9, 2024

@voronianski - good point, let me try out a couple of ideas and I'll get back to you - I'm thinking we can track it via an isAnimating state and only render the empty <View /> when isAnimating is false.

from stargrave-0.

brentvatne avatar brentvatne commented on June 9, 2024

@voronianski - the below code works except that animating to opacity: 0 seems to be broken - it just immediately goes to 0. If you set opacity to 0.5 it will animate from 0 to 0.5 even when it's already visible. Perhaps we could look at contributing back a fix for that. But if you replace that with, eg, position: [-100, -100] you will that this solution works for hide animations in general. Ideally startAnimation would support an onComplete callback so we do not have to use setTimeout - perhaps another patch we could look at contributing. Have a look and let me know what you think.

var Modal = React.createClass({
  propTypes: {
    isVisible: PropTypes.bool,
    hideCloseButton: PropTypes.bool,
    customCloseButton: PropTypes.node,
    onClose: PropTypes.func,
    customShowHandler: PropTypes.func,
    customHideHandler: PropTypes.func,
  },

  getInitialState() {
    return {
      isTransitioning: false,
    }
  },

  componentWillReceiveProps(nextProps) {
    var willBeVisible = nextProps.isVisible;
    var isVisible = this.props.isVisible;

    if (willBeVisible !== isVisible) {
      this.setState({isTransitioning: true});
    }
  },

  componentDidUpdate(prevProps) {
    var { isVisible, customShowHandler, customHideHandler } = this.props;
    var isTransitioning = this.state.isTransitioning;

    var nodeRef = this.refs['this'];
    var onCompleteFn = () => {
      this.setState({isTransitioning: false});
    }

    if (!isTransitioning) return;

    if (isVisible) {
      if (customShowHandler) {
          return customShowHandler(nodeRef, onCompleteFn);
      } else {
          Animation.startAnimation(nodeRef, 300, 0, 'easeInOutQuad', {opacity: 1});
          setTimeout(onCompleteFn, 300);
      }
    } else {
      if (customHideHandler) {
          return customHideHandler(nodeRef, onCompleteFn);
      } else {

          Animation.startAnimation(this.refs['this'], 300, 0, 'easeInOutQuad', {opacity: 0});
          setTimeout(onCompleteFn, 300);
      }
    }
  },

  render() {
    var {
      hideCloseButton,
      customCloseButton,
      isVisible,
      onClose,
      children
    } = this.props;

    var isTransitioning = this.state.isTransitioning;

    var closeButton;
    if (customCloseButton) {
      closeButton = React.addons.cloneWithProps(customCloseButton, null);
    } else if (!hideCloseButton && onClose) {
      closeButton = (
        <View style={modalStyles.closeButton}>
          <TouchableOpacity onPress={onClose}>
            <Text style={modalStyles.closeButtonText}>Close</Text>
          </TouchableOpacity>
        </View>
      );
    }

    if (isVisible || isTransitioning) {
      return (
        <View ref="this" style={modalStyles.container}>
          <View style={modalStyles.backdrop} />
          {closeButton}
          <View style={modalStyles.modal}>
            {React.Children.map(children, React.addons.cloneWithProps)}
          </View>
        </View>
      );
    } else {
      return <View />;
    }
  },
});

from stargrave-0.

voronianski avatar voronianski commented on June 9, 2024

@brentvatne thanks I will try it tomorrow (it's 1.00 am in my timezone 😄 ).

from stargrave-0.

voronianski avatar voronianski commented on June 9, 2024

@brentvatne your example works though opacity doesn't - probably we need to raise an issue. And regarding timers, have you seen this doc - http://facebook.github.io/react-native/docs/timers.html#content TimerMixin should be helpful here?

from stargrave-0.

voronianski avatar voronianski commented on June 9, 2024

@brentvatne also I think we need to extend some features of the module - backdrop should be optional, it could have separate animation, and may handle press - #3

from stargrave-0.

brentvatne avatar brentvatne commented on June 9, 2024

@voronianski - great call on TimerMixin, definitely a better choice than just setTimeout 🎱

I found out why animating to opacity 0 isn't working - gist of log statements; no matter what the fromValue (value of opacity when starting animation) is set to, it's always passed into the easing function builder as 0.0 due to what appears to be an obscure obj-c typing bug (my knowledge of obj-c is but a week old so I was unable to fix this on my own) - I'll try fixing it with discussions in IRC and if that doesn't work then I'll create an issue.

from stargrave-0.

brentvatne avatar brentvatne commented on June 9, 2024

@voronianski - I spoke with @vjeux in irc and he mentioned that the current animation module was put together in a single day, and that the react-native team is considering removing it until they have time to build a more robust module. He referred me to react-tween-state as an alternative - so I went ahead and replaced the animation implementation:

// ** EDITED: THE CODE BELOW IS OUTDATED!! **
'use strict';

var React = require('react-native');
var {
  StyleSheet,
  View,
  Text,
  TouchableOpacity,
  PropTypes,
} = React;

var TweenState = require('react-tween-state');
var styles = require('./Style');
var merge = require('merge');

var Modal = React.createClass({
  mixins: [TweenState.Mixin],
  statics: {
    easingTypes: TweenState.easingTypes,
  },

  propTypes: {
    isVisible: PropTypes.bool,
    hideCloseButton: PropTypes.bool,
    onClose: PropTypes.func,
    customCloseButton: PropTypes.node,
    customShowHandler: PropTypes.func,
    customHideHandler: PropTypes.func,
  },

  getInitialState() {
    return {
      isTransitioning: false,
    }
  },

  componentWillReceiveProps(nextProps) {
    var willBeVisible = nextProps.isVisible;
    var isVisible = this.props.isVisible;

    if (willBeVisible !== isVisible) {
      if (willBeVisible) {
        var showHandler = this.props.customShowHandler || ((t) => t('opacity', {duration: 300, begin: 0, end: 1}))
        showHandler(this.transition);
      } else {
        var hideHandler = this.props.customHideHandler || ((t) => t('opacity', {duration: 300, end: 0}))
        hideHandler(this.transition);
      }
    }
  },

  transition(property, options) {
    this.setState({isTransitioning: true});

    this.tweenState(property, {
      easing: options.easing || Modal.easingTypes.easeInOutQuad,
      duration: options.duration || 300,
      beginValue: (typeof options.begin === 'undefined' ? this.state[property] : options.begin),
      endValue: options.end,
      onEnd: (() => {
        if (this.state.tweenQueue.length === 1) this.setState({isTransitioning: false})
        if (options.onEnd) onEnd();
      }),
    });
  },

  transitionStyles(propertySet) {
    if (this.state.tweenQueue.length === 0) return {};
    if (typeof propertySet === 'undefined') propertySet = [];
    var result = {};

    this.state.tweenQueue.forEach((tween) => {
      var property = tween.stateName;
      if (propertySet.length === 0 || propertySet.indexOf(property) > -1) {
        var value = this.getTweeningValue(property);
        result[property] = value;
      }
    });

    return result;
  },

  render() {
    var {
      hideCloseButton,
      customCloseButton,
      isVisible,
      onClose,
      children
    } = this.props;

    var closeButton;
    if (customCloseButton) {
      closeButton = React.addons.cloneWithProps(customCloseButton, null);
    } else if (!hideCloseButton && onClose) {
      closeButton = (
        <View style={styles.closeButton}>
          <TouchableOpacity onPress={onClose}>
            <Text style={styles.closeButtonText}>Close</Text>
          </TouchableOpacity>
        </View>
      );
    }

    if (isVisible || this.state.isTransitioning) {
      return (
        <View ref="this" style={[styles.container, this.transitionStyles()]}>
          <View style={styles.backdrop} />
          {closeButton}
          <View style={styles.modal}>
            {React.Children.map(children, React.addons.cloneWithProps)}
          </View>
        </View>
      );
    } else {
      return <View />;
    }
  },
});

module.exports = Modal;

Custom show and hide handlers would be functions like:

customHideHandler={(transition) => transition('left', {duration: 500, begin: -100, end: 100})}

These can also be combined, you can call transition multiple times. What do you think of this solution?

from stargrave-0.

brentvatne avatar brentvatne commented on June 9, 2024

@voronianski - I went ahead and updated the code with a modified version of above, also using a react-native-tween-state wrapper mixin that I put together here, as it seemed like I was beginning to complect the modal component with a bunch of animation logic. Feel free to re-open this issue if this solution isn't to your liking!

from stargrave-0.

voronianski avatar voronianski commented on June 9, 2024

@brentvatne I like how it works right now! Though I'm trying to implement some bouncing easing and it doesn't work as expected with Modal.transitionEasings.easeInBounce. Anyway 👍

from stargrave-0.

brentvatne avatar brentvatne commented on June 9, 2024

@voronianski - | could hardly notice the effect at a short time duration, but when I bumped the animation speed up to say 1000ms+ it looked to be working correctly for me - I think with these kind of easings you have to be sure to give it more time otherwise it just looks twitchy.

For example, if you replace this code with the following, you should see the effect in action :)

  showModalTransition(transition) {
    transition('opacity', {duration: 1500, begin: 0, end: 1, easing: Modal.transitionEasings.easeInBounce});
    transition('height', {duration: 1500, begin: DeviceHeight * 2, end: DeviceHeight, easing: Modal.transitionEasings.easeInBounce});
  },

  hideModalTransition(transition) {
    transition('height', {duration: 1000, begin: DeviceHeight, end: DeviceHeight * 2, reset: true, easing: Modal.transitionEasings.easeInBounce});
    transition('opacity', {duration: 1000, begin: 1, end: 0, easing: Modal.transitionEasings.easeInBounce});
  },

from stargrave-0.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.