FluidTransitions icon indicating copy to clipboard operation
FluidTransitions copied to clipboard

Lag with transitioning from one screen to another

Open cbrewer1689 opened this issue 6 years ago • 4 comments

Upon an action taking us from one screen to another there is some lag in the transitioning. This happens both on iPhone and Android emulators and as well on real devices. Debugging disabled and slow motion disabled as well. The older the device is (by its release date), the longer the lag. e.g. On a iPhone 6+ there is about a 4 second lag versus the iPhone X's, maybe a second lag.

We're using:

  • react v.16.6.0
  • react-native v.0.57.5
  • react-navigation-fluid-transitions v.0.2.74

Code below sort of gives an example of code going from the Feed, to My Profile, to About Me. I excluded some code, but left their functions.

./navigator.js

import feed from './feed/feed.js';
import { FluidNavigator } from 'react-navigation-fluid-transitions';
import myStuffMyProfile from './myStuff/myProfile.js';
import myStuffMyProfileAboutMe from './myStuff/myProfileAboutMe.js';

const Navigator = FluidNavigator({
    feed: {
        screen: feed
    },
    myStuffMyProfile: {
        screen: myStuffMyProfile,
    },
    myStuffMyProfileAboutMe: {
        screen: myStuffMyProfileAboutMe,
    },
}, {
    initialRouteName: 'feed',
    navigationOptions: { gesturesEnabled: false },
});

export default Navigator;

./app/app.js

import { Dimensions, Image, StatusBar, View, AsyncStorage } from 'react-native';
import React, { Component } from 'react';
import bootstrapActions from '../shared/bootstrapActions.js';
import { connect } from 'react-redux';
import enumerations from '../shared/enumerations.js';
import loginActions from '../login/loginActions.js';
import Navigation from './navigation.js';
import navigationService from '../shared/navigationService.js';
import Navigator from '../navigator.js';
import styles from './appStyles.js';

class App extends Component {
    getState = async () => {
        // excluded
    }

    constructor() {
        super();

        this.state = {
            currentRoute: 'feed',
        };
    }

    componentWillMount () {
        // excluded
    }

    componentDidUpdate(prevProps, prevState) {
        // excluded
    }

    render () {
        const { showNavigation } = this.props;
        const { currentRoute } = this.state;

        return (
            <View style={styles.applicationView}>
                <StatusBar backgroundColor="black" barStyle="dark-content" translucent />

                <View
                    style={{
                        height: Dimensions.get('window').height,
                        position: 'relative',
                        width: Dimensions.get('window').width,
                        zIndex: 0
                    }}
                >
                    <Navigator
                        onNavigationStateChange={(prevState, currentState) => this._getActiveRouteName(currentState)}
                        ref={navigatorRef => {
                            navigationService.setTopLevelNavigator(navigatorRef);
                        }}
                    />
                </View>

                {showNavigation ? (
                    <Navigation
                        currentRoute={currentRoute}
                        sections={[ static data right now ]}
                        zIndex={1}
                    />
                ) : null}
            </View>
        );
    }

    _getActiveRouteName(navigationState) {
        // excluded
    }
}

const mapStateToProps = (state) => {
        // excluded
};

export default connect(mapStateToProps)(App);

./myStuff/myProfile.js

import { Card, Header } from '../shared/ui';
import { Container, Content, Text } from 'native-base';
import { Dimensions, Image, TouchableOpacity, View } from 'react-native';
import _ from 'lodash';
import aboutMeCardIcon from './images/about_me-card_icon.png';
import aboutMeCardImage from './images/about_me-card_image.jpg';
import { colors } from '../shared/styles/index.js';
import { connect } from 'react-redux';
import contactInfoCardIcon from './images/contact_info-card_icon.png';
import contactInfoCardImage from './images/contact_info-card_image.jpg';
import FastImage from 'react-native-fast-image';
import { font } from '../shared/styles';
import myProfileActions from './myProfileActions.js';
import navigationActions from '../app/navigationActions.js';
import navigationService from '../shared/navigationService.js';
import occupationCardIcon from './images/occupation-card_icon.png';
import occupationCardImage from './images/occupation-card_image.jpg';
import personProfileActions from '../shared/personProfileActions';
import React from 'react';
import { Transition } from 'react-navigation-fluid-transitions';

const deviceScreen = Dimensions.get('window');

class MyProfile extends React.PureComponent {
    constructor() {
        super();

        this._onCardClick = this._onCardClick.bind(this);
    }

    render() {
        const { credentials: { isLoggedIn }, person } = this.props;

        if (!isLoggedIn || _.isEmpty(person)) {
            return false;
        }

        const avatarURI = 'https://images.unsplash.com/photo-1531439447095-467ba6360738?ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80';

        return (
            <Container>
                <Transition inline shared="myProfileHeaderBkgd">
                    <Image
                        source={{ uri: avatarURI }}
                        style={{
                            flex: 1,
                            height: 406,
                            resizeMode: 'cover',
                            width: '100%',
                        }}
                    />
                </Transition>

                <Transition inline shared="myProfileHeader">
                    <View
                        style={{
                            backgroundColor: 'rgba(23, 182, 210, .5)',
                            height: 94,
                            position: 'absolute',
                            transform: [
                                { translateX: 0 },
                                { translateY: 0 }
                            ],
                            width: '100%',
                        }}
                    >
                        <Header
                            color={colors.text.inverse}
                            label="My Profile"
                            statusBarStyle="light-content"
                        />
                    </View>
                </Transition>

                <Content
                    style={{
                        paddingBottom: 33,
                        paddingLeft: 11,
                        paddingRight: 11,
                        paddingTop: 33,
                    }}
                >
                    <View
                        style={{
                            alignItems: 'flex-start',
                            flex: 1,
                            flexDirection: 'row',
                            flexWrap: 'wrap',
                        }}
                    >
                        <View
                            style={{
                                paddingBottom: 16,
                                paddingLeft: 11,
                                paddingRight: 11,
                                width: deviceScreen.width / 2 - 11,
                            }}
                        >
                            <TouchableOpacity
                                activeOpacity={.9}
                                onPress={() => this._onCardClick('myStuffMyProfileAboutMe')}
                            >
                                <Transition inline shared="aboutMeCard">
                                    <Card borderRadius={5}>
                                        <FastImage
                                            resizeMode={FastImage.resizeMode.cover}
                                            source={aboutMeCardImage}
                                            style={{
                                                borderRadius: 5,
                                                height: 120,
                                                width: '100%',
                                            }}
                                        />

                                        <FastImage
                                            source={aboutMeCardIcon}
                                            style={{
                                                alignSelf: 'center',
                                                bottom: 0,
                                                flex: 1,
                                                height: 84,
                                                position: 'absolute',
                                                transform: [
                                                    { translateY: -18 },
                                                ],
                                                width: 84,
                                            }}
                                        />
                                    </Card>
                                </Transition>

                                <Text
                                    style={{
                                        ...font.family.bold,
                                        marginTop: -1,
                                    }}
                                >
                                    {_.upperCase('About Me')}
                                </Text>
                            </TouchableOpacity>
                        </View>

                        <View
                            style={{
                                paddingBottom: 16,
                                paddingLeft: 11,
                                paddingRight: 11,
                                width: deviceScreen.width / 2 - 11,
                            }}
                        >
                            <TouchableOpacity
                                activeOpacity={.9}
                                onPress={() => this._onCardClick('myStuffMyProfileContactInfo')}
                            >
                                <Transition inline shared="contactInfoCard">
                                    <Card>
                                        <FastImage
                                            source={contactInfoCardImage}
                                            style={{
                                                borderRadius: 5,
                                                height: 120,
                                                resizeMode: 'cover',
                                                width: '100%',
                                            }}
                                        />

                                        <FastImage
                                            source={contactInfoCardIcon}
                                            style={{
                                                alignSelf: 'center',
                                                bottom: 0,
                                                flex: 1,
                                                height: 69,
                                                position: 'absolute',
                                                transform: [
                                                    { translateY: -26 },
                                                ],
                                                width: 86,
                                            }}
                                        />
                                    </Card>
                                </Transition>

                                <Text
                                    style={{
                                        ...font.family.bold,
                                        marginTop: -1,
                                    }}
                                >
                                    {_.upperCase('Contact Info')}
                                </Text>
                            </TouchableOpacity>
                        </View>

                        <View
                            style={{
                                paddingBottom: 11,
                                paddingLeft: 11,
                                paddingRight: 11,
                                width: deviceScreen.width / 2 - 11,
                            }}
                        >
                            <TouchableOpacity
                                activeOpacity={.9}
                                onPress={() => this._onCardClick('myStuffMyProfileOccupation')}
                            >
                                <Transition inline shared="occupationCard">
                                    <Card>
                                        <FastImage
                                            source={occupationCardImage}
                                            style={{
                                                borderRadius: 5,
                                                height: 120,
                                                resizeMode: 'cover',
                                                width: '100%',
                                            }}
                                        />

                                        <FastImage
                                            source={occupationCardIcon}
                                            style={{
                                                alignSelf: 'center',
                                                bottom: 0,
                                                flex: 1,
                                                height: 86,
                                                position: 'absolute',
                                                transform: [
                                                    { translateY: -20 },
                                                ],
                                                width: 86,
                                            }}
                                        />
                                    </Card>
                                </Transition>

                                <Text
                                    style={{
                                        ...font.family.bold,
                                        marginTop: -1,
                                    }}
                                >
                                    {_.upperCase('Occupation')}
                                </Text>
                            </TouchableOpacity>
                        </View>
                    </View>
                </Content>
            </Container>
        );
    }

    componentDidMount() {
        // excluded, conditions that reroute if not logged in.
    }

    _onCardClick(to) {
        navigationActions.toggleVisibility(false);
        navigationService.navigate(to);
    }

    _onSwapAvatar(avatar) {
        // excluded
    }
}

const mapStateToProps = (state) => {
        // excluded
};

export default connect(mapStateToProps)(MyProfile);

./myStuff/myProfileAboutMe.js

import { Animated, Dimensions, Image, View } from 'react-native';
import { Button, Card, Header, HeaderScrollView } from '../shared/ui';
import { DatePicker, Form, Icon, Input, Item, Label, Picker, StyleProvider, Text } from 'native-base';
import _ from 'lodash';
import aboutMeCardIcon from './images/about_me-card_icon.png';
import aboutMeCardImage from './images/about_me-card_image.jpg';
import { colors } from '../shared/styles/index.js';
import { connect } from 'react-redux';
import enumerations from '../shared/enumerations.js';
import FastImage from 'react-native-fast-image';
import { font } from '../shared/styles/index.js';
import getTheme from '../shared/native-base-theme/components/index.js';
import hcMobileApp from '../shared/native-base-theme/variables/hcMobileApp.js';
import myProfileActions from './myProfileActions.js';
import navigationActions from '../app/navigationActions.js';
import navigationService from '../shared/navigationService.js';
import React from 'react';
import { Transition } from 'react-navigation-fluid-transitions';

const deviceScreen = Dimensions.get('window');
const AnimatedFastImage = Animated.createAnimatedComponent(FastImage);

class MyProfileAboutMe extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            genderOptions: enumerations.getGenders(),
            gradeOptions: enumerations.getGradesSelect(),
            maritalStatusOptions: enumerations.getMaritalStatus(),
            newContactPrefernces: {},
            newPerson: {},
            personId: props.person.id,
            scrollY: new Animated.Value(0),
        };

        this._getPersonValue = this._getPersonValue.bind(this);
        this._onSavePress = this._onSavePress.bind(this);
    }

    componentDidUpdate(prevProps, prevState) {
        const { person } = this.props;

        if (prevProps.person.id !== person.id) {
            this.setState({
                personId: person.id,
            });
        }
    }

    render() {
        const { campusOptions, person } = this.props;

        if (_.isEmpty(person)) { return false; }

        const { genderOptions, gradeOptions, maritalStatusOptions } = this.state;
        const avatarURI = 'https://images.unsplash.com/photo-1531439447095-467ba6360738?ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80';
        const headerMinHeight = 94;
        const headerMaxHeight = 214;
        const footerHeight = 105;

        return (
            <StyleProvider style={getTheme(hcMobileApp)}>
                <View
                    style={{
                        height: deviceScreen.height,
                    }}
                >
                    <HeaderScrollView
                        label="About Me"
                        onClosePress={this._onClosePress}
                        onScroll={this._onScroll}
                        renderHeader={() => (
                            <View>
                                <Transition inline shared="myProfileHeaderBkgd">
                                    <Image
                                        blurRadius={4}
                                        source={{ uri: avatarURI }}
                                        style={{
                                            flex: 1,
                                            height: headerMaxHeight,
                                            position: 'absolute',
                                            resizeMode: 'cover',
                                            transform: [
                                                { translateX: 0 },
                                                { translateY: 0 }
                                            ],
                                            width: '100%',
                                        }}
                                    />
                                </Transition>

                                <Transition inline shared="myProfileHeader">
                                    <View
                                        style={{
                                            backgroundColor: 'rgba(23, 182, 210, .5)',
                                            height: headerMaxHeight,
                                            position: 'absolute',
                                            transform: [
                                                { translateX: 0 },
                                                { translateY: 0 }
                                            ],
                                            width: '100%',
                                        }}
                                    >
                                        <Header
                                            color={colors.text.inverse}
                                            height={headerMaxHeight}
                                            hideStatusBar
                                        />
                                    </View>
                                </Transition>
                            </View>
                        )}
                        renderCard={() => {
                            const { scrollY } = this.state;
                            const cardTopPosition = headerMaxHeight - (120 - 16);
                            const cardHeight = scrollY.interpolate({
                                inputRange: [ 0, cardTopPosition / 3, cardTopPosition / 2, cardTopPosition / 1.6, cardTopPosition ],
                                outputRange: [ 120, 120, 170, 170, headerMinHeight ],
                                extrapolate: 'clamp',
                            });
                            const cardIconSize = scrollY.interpolate({
                                inputRange: [ cardTopPosition / 1.6, cardTopPosition ],
                                outputRange: [ 84, 0 ],
                                extrapolate: 'clamp',
                            });
                            const cardIconTranslateY = scrollY.interpolate({
                                inputRange: [ cardTopPosition / 3, cardTopPosition / 2, cardTopPosition / 1.6, cardTopPosition ],
                                outputRange: [ -18, -8, -8, 0 ],
                                extrapolate: 'clamp',
                            });

                            return (
                                <Transition inline shared="aboutMeCard">
                                    <Card borderRadius={5}>
                                        <AnimatedFastImage
                                            resizeMode={FastImage.resizeMode.cover}
                                            source={aboutMeCardImage}
                                            style={{
                                                borderRadius: 5,
                                                height: cardHeight,
                                                width: '100%',
                                            }}
                                        />

                                        <AnimatedFastImage
                                            source={aboutMeCardIcon}
                                            style={{
                                                alignSelf: 'center',
                                                bottom: 0,
                                                flex: 1,
                                                height: cardIconSize,
                                                position: 'absolute',
                                                transform: [
                                                    { translateY: cardIconTranslateY },
                                                ],
                                                width: cardIconSize,
                                            }}
                                        />
                                    </Card>
                                </Transition>
                            );
                        }}
                    >
                        <View
                            style={{
                                paddingBottom: footerHeight + 22,
                                paddingLeft: 22,
                                paddingTop: 22,
                                paddingRight: 22,
                            }}
                        >
                            <Form>
                                <Item floatingLabel>
                                    <Label>Title</Label>

                                    <Input
                                        onChangeText={(text) => this._setPersonValue('prefix', text)}
                                        value={this._getPersonValue('prefix')}
                                    />
                                </Item>

                                <Item floatingLabel>
                                    <Label>First Name</Label>

                                    <Input
                                        onChangeText={(text) => this._setPersonValue('firstName', text)}
                                        value={this._getPersonValue('firstName')}
                                    />
                                </Item>

                                <Item floatingLabel>
                                    <Label>Last Name</Label>

                                    <Input
                                        onChangeText={(text) => this._setPersonValue('lastName', text)}
                                        value={this._getPersonValue('lastName')}
                                    />
                                </Item>

                                <Item floatingLabel>
                                    <Label>Middle Name</Label>

                                    <Input
                                        onChangeText={(text) => this._setPersonValue('middleName', text)}
                                        value={this._getPersonValue('middleName')}
                                    />
                                </Item>

                                <Item floatingLabel>
                                    <Label>Nickname</Label>

                                    <Input
                                        onChangeText={(text) => this._setPersonValue('nickName', text)}
                                        value={this._getPersonValue('nickName')}
                                    />
                                </Item>

                                <Item floatingLabel>
                                    <Label>Suffix</Label>

                                    <Input
                                        onChangeText={(text) => this._setPersonValue('suffix', text)}
                                        value={this._getPersonValue('suffix')}
                                    />
                                </Item>

                                <Item
                                    pickerFloatingLabel
                                >
                                    <Label>Gender</Label>

                                    <Picker
                                        iosIcon={<Icon name="ios-arrow-down" />}
                                        onValueChange={(value) => this._setPersonValue('gender', value)}
                                        placeholder="Select your gender"
                                        selectedValue={this._getPersonValue('gender')}
                                        textStyle={{ color: 'green', ...font.family.bold, fontSize: 18 }}
                                        itemStyle={{ color: 'green', }}
                                    >
                                        {_.map(genderOptions, gender => {
                                            return (
                                                <Picker.Item
                                                    key={_.kebabCase(gender.value)}
                                                    label={gender.label}
                                                    value={gender.value}
                                                />
                                            );
                                        })}
                                    </Picker>
                                </Item>

                                <Item
                                    pickerFloatingLabel
                                >
                                    <Label>Marital Status</Label>

                                    <Picker
                                        iosIcon={<Icon name="ios-arrow-down" />}
                                        onValueChange={(value) => this._setPersonValue('maritalStatusId', value)}
                                        placeholder="Select your marital status"
                                        selectedValue={this._getPersonValue('maritalStatusId')}
                                        textStyle={{ ...font.family.bold, color: '#ff0000', fontSize: 18 }}
                                    >
                                        {_.map(maritalStatusOptions, status => {
                                            return (
                                                <Picker.Item
                                                    key={_.kebabCase(status.value)}
                                                    label={status.label}
                                                    value={status.value}
                                                />
                                            );
                                        })}
                                    </Picker>
                                </Item>

                                <Item
                                    pickerFloatingLabel
                                >
                                    <Label>Birth Date</Label>

                                    <DatePicker
                                        defaultDate={new Date(2018, 4, 4)}
                                        minimumDate={new Date(1900, 1, 1)}
                                        maximumDate={new Date()}
                                        locale="en"
                                        timeZoneOffsetInMinutes={undefined}
                                        modalTransparent={false}
                                        animationType="fade"
                                        androidMode="default"
                                        placeHolderText="Select date"
                                        onDateChange={(date) => this._setPersonDateValue('birthDate', date)}
                                        placeHolderTextStyle={{
                                            ...font.family.bold,
                                            height: 36,
                                            fontSize: 18,
                                            padding: 0,
                                            paddingBottom: 5,
                                            textAlignVertical: 'bottom',
                                        }}
                                        textStyle={{
                                            ...font.family.bold,
                                            height: 36,
                                            fontSize: 18,
                                            padding: 0,
                                            paddingBottom: 5,
                                            textAlignVertical: 'bottom',
                                        }}
                                    />
                                </Item>

                                <Item
                                    pickerFloatingLabel
                                >
                                    <Label>Campus</Label>

                                    <Picker
                                        iosIcon={<Icon name="ios-arrow-down" />}
                                        onValueChange={(value) => this._setCampus(value)}
                                        placeholder="Select your campus"
                                        selectedValue={this._getPersonValue('churchEntityId')}
                                        textStyle={{ ...font.family.bold, fontSize: 18 }}
                                    >
                                        {_.map(campusOptions, campus => {
                                            return (
                                                <Picker.Item
                                                    key={_.kebabCase(campus.value)}
                                                    label={campus.label}
                                                    value={campus.value}
                                                />
                                            );
                                        })}
                                    </Picker>
                                </Item>

                                <Item floatingLabel>
                                    <Label>Allergies</Label>

                                    <Input
                                        onChangeText={(text) => this._setPersonValue('allergies', text)}
                                        value={this._getPersonValue('allergies')}
                                    />
                                </Item>

                                <Item
                                    pickerFloatingLabel
                                >
                                    <Label>Grade</Label>

                                    <Picker
                                        iosIcon={<Icon name="ios-arrow-down" />}
                                        onValueChange={(value) => this._setPersonValue('gradeLevel', value)}
                                        placeholder="Select your grade"
                                        selectedValue={this._getPersonValue('gradeLevel')}
                                        textStyle={{ ...font.family.bold, fontSize: 18 }}
                                    >
                                        {_.map(gradeOptions, grade => {
                                            return (
                                                <Picker.Item
                                                    key={_.kebabCase(grade.value)}
                                                    label={grade.label}
                                                    value={grade.value}
                                                />
                                            );
                                        })}
                                    </Picker>
                                </Item>
                            </Form>
                        </View>
                    </HeaderScrollView>

                    <View
                        style={{
                            height: footerHeight,
                            paddingBottom: 44,
                            paddingLeft: 22,
                            paddingRight: 22,
                            paddingTop: 11,
                            position: 'absolute',
                            transform: [
                                { translateX: 0 },
                                { translateY: deviceScreen.height - footerHeight }
                            ],
                            width: deviceScreen.width
                        }}
                    >
                        <Button
                            full
                            onPress={this._onSavePress}
                            shadow
                        >
                            <Text>Save</Text>
                        </Button>
                    </View>
                </View>
            </StyleProvider>
        );
    }

    _dateToUnix(date) {
        // excluded
    }

    _getPreferredContact(key) {
        // excluded
    }

    _getPersonDateValue(dateKey, age = false) {
        // excluded
    }

    _getPersonValue(key) {
        // excluded 
    }

    _getPersonContactValue(key, displayOnly = false) {
        // excluded
    }

    _onClosePress = () => {
        navigationActions.toggleVisibility(true);
    }

    _onSavePress() {
        // excluded
    }

    _onScroll = (e) => {
        const { scrollY } = this.state;
        const newScrollY = e.nativeEvent.contentOffset.y;

        scrollY.setValue(newScrollY);
    };

    _setCampus(value) {
        // excluded
    }

    _setPersonDateValue(key, value) {
        // excluded
    }

    _setPersonValue(key, value) {
        // excluded
    }
}

const mapStateToProps = (state) => {
        // excluded
};

export default connect(mapStateToProps)(MyProfileAboutMe);

cbrewer1689 avatar Jan 12 '19 00:01 cbrewer1689

Another finding which may help, enabling "Debug JS Remotely" on my Android phone does speed up the transitions. Other animations slow down, of course, but the transition from my profile to about me goes from around 5-6 seconds to about 1 second or so. A couple issues people have recorded are console.log will slow down the app. I went through the app and made sure there are no console.log console.warn or console.error being rendered. I was able to comment out a bunch, but they didn't have much to do with the screens I'm having trouble with. Also some other people have said that redux-logger was the culprit for them, but we are not using that.

cbrewer1689 avatar Jan 13 '19 00:01 cbrewer1689

Мне приятно сейчас

вс, 13 янв. 2019 г., 2:57 Cameron Brewer [email protected]:

Another finding which may help, enabling "Debug JS Remotely" on my Android phone does speed up the transitions. Other animations slow down, of course, but the transition from my profile to about me goes from around 5-6 seconds to about 1 second or so. A couple issues people have recorded are console.log will slow down the app. I went through the app and made sure there are no console.log console.warn or console.error being rendered. I was able to comment out a bunch, but they didn't have much to do with the screens I'm having trouble with. Also some other people have said that redux-logger was the culprit for them, but we are not using that.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/fram-x/FluidTransitions/issues/155#issuecomment-453793114, or mute the thread https://github.com/notifications/unsubscribe-auth/ArfwBwVXOl0BOl7ndL00Vh1RkV0t9NYNks5vCoRygaJpZM4Z8Y_C .

Natalia844 avatar Jan 13 '19 00:01 Natalia844

Hi @morethanfire - thanks for the issue. Could you please try to create a simple reproducible snack showing this behavior?

chrfalch avatar Feb 09 '19 12:02 chrfalch

I can provide that in a few days. Thank you.

cbrewer1689 avatar Feb 14 '19 21:02 cbrewer1689