diff options
| author | anderklander <anderklander@gmail.com> | 2018-03-19 16:47:27 +0100 |
|---|---|---|
| committer | Andrej Mihajlov <and@mullvad.net> | 2018-03-26 14:22:11 +0200 |
| commit | 30b6dbe0caf3166ed4dd73e8c6c84289c2ea4892 (patch) | |
| tree | eff01eaf68223fa3a96c6449a3fa10b9b029e126 /app/components | |
| parent | 962d6e5df554ba67169100b59bada2c754a63713 (diff) | |
| download | mullvadvpn-30b6dbe0caf3166ed4dd73e8c6c84289c2ea4892.tar.xz mullvadvpn-30b6dbe0caf3166ed4dd73e8c6c84289c2ea4892.zip | |
Add animation for sign-in button
Diffstat (limited to 'app/components')
| -rw-r--r-- | app/components/Login.js | 75 | ||||
| -rw-r--r-- | app/components/LoginStyles.js | 6 |
2 files changed, 59 insertions, 22 deletions
diff --git a/app/components/Login.js b/app/components/Login.js index fecb5007a9..76e1bf1624 100644 --- a/app/components/Login.js +++ b/app/components/Login.js @@ -29,9 +29,11 @@ type State = { footerHeight: number, animatedFooterValue: Animated.Value, animatedDropdownValue: Animated.Value, - animation: Animated.CompositeAnimation, - footerAnimationStyle: Animated.Style, - dropdownAnimationStyle: Animated.Style, + animatedLoginButtonValue: Animated.Value, + animation: ?Animated.CompositeAnimation, + footerAnimationStyle: ?Animated.Style, + dropdownAnimationStyle: ?Animated.Style, + loginButtonAnimationStyle: ?Animated.Style, }; export default class Login extends Component<LoginPropTypes, State> { @@ -42,9 +44,11 @@ export default class Login extends Component<LoginPropTypes, State> { footerHeight: 0, animatedFooterValue: Animated.createValue(0), animatedDropdownValue: Animated.createValue(0), + animatedLoginButtonValue: Animated.createValue(0), animation: null, footerAnimationStyle: null, dropdownAnimationStyle: null, + loginButtonAnimationStyle: null, }; constructor(props: LoginPropTypes) { @@ -58,6 +62,9 @@ export default class Login extends Component<LoginPropTypes, State> { this.state.footerAnimationStyle = Styles.createAnimatedViewStyle({ transform: [{translateY: this.state.animatedFooterValue }] }); + this.state.loginButtonAnimationStyle = Styles.createAnimatedViewStyle({ + backgroundColor: Animated.interpolate(this.state.animatedLoginButtonValue, [0.0, 1.0], [colors.white, colors.green]), + }); } componentWillReceiveProps(nextProps: LoginPropTypes) { @@ -106,7 +113,7 @@ export default class Login extends Component<LoginPropTypes, State> { const relatedTarget = e.relatedTarget; // restore focus if click happened within dropdown - if(relatedTarget && this._isWithinDropdown(relatedTarget)) { + if(relatedTarget) { e.target.focus(); return; } @@ -122,11 +129,12 @@ export default class Login extends Component<LoginPropTypes, State> { } const footerPosition = this._shouldShowFooter(props) ? 0 : this.state.footerHeight; const dropdownHeight = this._shouldShowAccountHistory(props) ? this.state.dropdownHeight : 0; - this._setAnimation(this._getFooterAnimation(footerPosition), this._getDropdownAnimation(dropdownHeight)); + const loginButtonValue = (props.account.accountToken && props.account.accountToken.length) > 0 ? 1 : 0; + this._setAnimation(this._getFooterAnimation(footerPosition), this._getDropdownAnimation(dropdownHeight), this._getLoginButtonAnimation(loginButtonValue)); } - _setAnimation = (footerAnimation: Animated.CompositeAnimation, dropdownAnimation: Animated.CompositeAnimation) => { - let compositeAnimation = Animated.parallel([ footerAnimation, dropdownAnimation ]); + _setAnimation = (footerAnimation: Animated.CompositeAnimation, dropdownAnimation: Animated.CompositeAnimation, loginButtonAnimation: Animated.CompositeAnimation) => { + let compositeAnimation = Animated.parallel([ footerAnimation, dropdownAnimation, loginButtonAnimation]); this.setState({animation: compositeAnimation}, () => { compositeAnimation.start(() => this.setState({ animation: null @@ -197,7 +205,7 @@ export default class Login extends Component<LoginPropTypes, State> { } } - _accountInputGroupClass(): Array<Object> { + _accountInputGroupStyles(): Array<Object> { const classes = [styles.account_input_group]; if(this.state.isActive) { classes.push(styles.account_input_group__active); @@ -215,7 +223,20 @@ export default class Login extends Component<LoginPropTypes, State> { return classes; } - _accountInputButtonClass(): Array<Object> { + _accountInputButtonStyles(): Array<Object> { + const { status } = this.props.account; + const classes = [styles.account_input_button]; + + if(status === 'logging in') { + classes.push(styles.account_input_button__invisible); + } + + classes.push(this.state.loginButtonAnimationStyle); + + return classes; + } + + _accountInputArrowStyles(): Array<Object> { const { accountToken, status } = this.props.account; const classes = [styles.account_input_button]; @@ -272,18 +293,30 @@ export default class Login extends Component<LoginPropTypes, State> { }); } - _onDropdownLayout = (layout) => { - this.setState({dropdownHeight: layout.height}); + _getLoginButtonAnimation(toValue: number){ + return Animated.timing(this.state.animatedLoginButtonValue, { + toValue: toValue, + easing: Animated.Easing.Linear(), + duration: 250, + useNativeDriver: true, + }); } - // returns true if DOM node is within dropdown hierarchy - _isWithinDropdown(relatedTarget) { - const dropdownElement = this._accountDropdownElement; - return dropdownElement && dropdownElement.contains(relatedTarget); + _getLoginArrowAnimation(toValue: number){ + return Animated.timing(this.state.animatedLoginArrowValue, { + toValue: toValue, + easing: Animated.Easing.Linear(), + duration: 250, + useNativeDriver: true, + }); + } + + _onDropdownLayout = (layout) => { + this.setState({dropdownHeight: layout.height}); } // container element used for measuring the height of the accounts dropdown - _accountDropdownElement: ?HTMLElement; + _accountDropdownElement: ?React.Node; _onAccountDropdownContainerRef = ref => this._accountDropdownElement = ref; _onSelectAccountFromHistory = (accountToken) => { @@ -303,9 +336,11 @@ export default class Login extends Component<LoginPropTypes, State> { } }; + + return <View style= {styles.login}> <Text style={ styles.subtitle }>{ this._formSubtitle() }</Text> - <View style={ this._accountInputGroupClass() }> + <View style={ this._accountInputGroupStyles() }> <View style={ styles.account_input_backdrop}> <AccountInput style={styles.account_input_textfield} type="text" @@ -320,9 +355,9 @@ export default class Login extends Component<LoginPropTypes, State> { autoFocus={ true } ref={ autoFocusOnFailure } testName='AccountInput'/> - <Button style={ this._accountInputButtonClass() } onPress={ this._onLogin } testName='account-input-button'> - <Img style={[ this._accountInputButtonClass() ]} source='icon-arrow' height='16' width='24' tintColor='currentColor' /> - </Button> + <Animated.View style={this._accountInputButtonStyles()} onPress={ this._onLogin } testName='account-input-button'> + <Img style={this._accountInputArrowStyles()} source='icon-arrow' height='16' width='24' tintColor='currentColor' /> + </Animated.View> </View> <Animated.View style={ this.state.dropdownAnimationStyle }> <View onLayout={this._onDropdownLayout} ref={ this._onAccountDropdownContainerRef }> diff --git a/app/components/LoginStyles.js b/app/components/LoginStyles.js index ea3a2db35c..ed079207bd 100644 --- a/app/components/LoginStyles.js +++ b/app/components/LoginStyles.js @@ -10,7 +10,7 @@ export default { login_footer: { backgroundColor: colors.darkBlue, paddingTop: 18, - paddingBottom:24, + paddingBottom:16, flex: 0, }, status_icon: { @@ -64,13 +64,15 @@ export default { border: 0, width: 48, alignItems: 'center', + justifyContent: 'center', color: colors.blue20, }, account_input_button__active: { color: colors.white, - backgroundColor: colors.green, }, account_input_button__invisible: { + color: colors.white, + backgroundColor: colors.white, visibility: 'hidden', opacity: 0, }, |
