FluidTransitions
FluidTransitions copied to clipboard
Lag with transitioning from one screen to another
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);
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.
Мне приятно сейчас
вс, 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 .
Hi @morethanfire - thanks for the issue. Could you please try to create a simple reproducible snack showing this behavior?
I can provide that in a few days. Thank you.