2016-04-25 07:08:42 +00:00
|
|
|
/**
|
2017-05-06 03:50:47 +00:00
|
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
2016-07-12 12:51:57 +00:00
|
|
|
*
|
2018-02-17 02:24:55 +00:00
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
2016-07-12 12:51:57 +00:00
|
|
|
*
|
2018-05-11 20:32:37 +00:00
|
|
|
* @format
|
2016-04-25 07:08:42 +00:00
|
|
|
* @flow
|
|
|
|
*/
|
2018-05-11 20:32:37 +00:00
|
|
|
|
2016-04-25 07:08:42 +00:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const React = require('react');
|
|
|
|
const ReactNative = require('react-native');
|
2018-05-11 20:32:37 +00:00
|
|
|
const {LayoutAnimation, StyleSheet, Text, View, TouchableOpacity} = ReactNative;
|
2016-04-25 07:08:42 +00:00
|
|
|
|
2017-08-18 01:36:54 +00:00
|
|
|
class AddRemoveExample extends React.Component<{}, $FlowFixMeState> {
|
2016-07-26 08:00:02 +00:00
|
|
|
state = {
|
|
|
|
views: [],
|
|
|
|
};
|
2016-04-25 07:08:42 +00:00
|
|
|
|
2018-02-08 18:26:45 +00:00
|
|
|
UNSAFE_componentWillUpdate() {
|
2016-04-25 07:08:42 +00:00
|
|
|
LayoutAnimation.easeInEaseOut();
|
2016-07-26 08:00:02 +00:00
|
|
|
}
|
2016-04-25 07:08:42 +00:00
|
|
|
|
2016-07-26 08:00:02 +00:00
|
|
|
_onPressAddView = () => {
|
2018-05-11 20:32:37 +00:00
|
|
|
this.setState(state => ({views: [...state.views, {}]}));
|
2016-07-26 08:00:02 +00:00
|
|
|
};
|
2016-04-25 07:08:42 +00:00
|
|
|
|
2016-07-26 08:00:02 +00:00
|
|
|
_onPressRemoveView = () => {
|
2018-05-11 20:32:37 +00:00
|
|
|
this.setState(state => ({views: state.views.slice(0, -1)}));
|
2016-07-26 08:00:02 +00:00
|
|
|
};
|
2016-04-25 07:08:42 +00:00
|
|
|
|
|
|
|
render() {
|
2018-05-11 20:32:37 +00:00
|
|
|
const views = this.state.views.map((view, i) => (
|
2016-04-25 07:08:42 +00:00
|
|
|
<View key={i} style={styles.view}>
|
|
|
|
<Text>{i}</Text>
|
|
|
|
</View>
|
2018-05-11 20:32:37 +00:00
|
|
|
));
|
2016-04-25 07:08:42 +00:00
|
|
|
return (
|
|
|
|
<View style={styles.container}>
|
|
|
|
<TouchableOpacity onPress={this._onPressAddView}>
|
|
|
|
<View style={styles.button}>
|
|
|
|
<Text>Add view</Text>
|
|
|
|
</View>
|
|
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity onPress={this._onPressRemoveView}>
|
|
|
|
<View style={styles.button}>
|
|
|
|
<Text>Remove view</Text>
|
|
|
|
</View>
|
|
|
|
</TouchableOpacity>
|
2018-05-11 20:32:37 +00:00
|
|
|
<View style={styles.viewContainer}>{views}</View>
|
2016-04-25 07:08:42 +00:00
|
|
|
</View>
|
|
|
|
);
|
2016-07-26 08:00:02 +00:00
|
|
|
}
|
|
|
|
}
|
2016-04-25 07:08:42 +00:00
|
|
|
|
2018-05-11 20:32:37 +00:00
|
|
|
const GreenSquare = () => (
|
2016-04-25 07:08:42 +00:00
|
|
|
<View style={styles.greenSquare}>
|
|
|
|
<Text>Green square</Text>
|
2018-05-11 20:32:37 +00:00
|
|
|
</View>
|
|
|
|
);
|
2016-04-25 07:08:42 +00:00
|
|
|
|
2018-05-11 20:32:37 +00:00
|
|
|
const BlueSquare = () => (
|
2016-04-25 07:08:42 +00:00
|
|
|
<View style={styles.blueSquare}>
|
|
|
|
<Text>Blue square</Text>
|
2018-05-11 20:32:37 +00:00
|
|
|
</View>
|
|
|
|
);
|
2016-04-25 07:08:42 +00:00
|
|
|
|
2017-08-18 01:36:54 +00:00
|
|
|
class CrossFadeExample extends React.Component<{}, $FlowFixMeState> {
|
2016-07-26 08:00:02 +00:00
|
|
|
state = {
|
|
|
|
toggled: false,
|
|
|
|
};
|
2016-04-25 07:08:42 +00:00
|
|
|
|
2016-07-26 08:00:02 +00:00
|
|
|
_onPressToggle = () => {
|
2016-04-25 07:08:42 +00:00
|
|
|
LayoutAnimation.easeInEaseOut();
|
2018-05-11 20:32:37 +00:00
|
|
|
this.setState(state => ({toggled: !state.toggled}));
|
2016-07-26 08:00:02 +00:00
|
|
|
};
|
2016-04-25 07:08:42 +00:00
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<View style={styles.container}>
|
|
|
|
<TouchableOpacity onPress={this._onPressToggle}>
|
|
|
|
<View style={styles.button}>
|
|
|
|
<Text>Toggle</Text>
|
|
|
|
</View>
|
|
|
|
</TouchableOpacity>
|
|
|
|
<View style={styles.viewContainer}>
|
2018-05-11 20:32:37 +00:00
|
|
|
{this.state.toggled ? <GreenSquare /> : <BlueSquare />}
|
2016-04-25 07:08:42 +00:00
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
);
|
2016-07-26 08:00:02 +00:00
|
|
|
}
|
|
|
|
}
|
2016-04-25 07:08:42 +00:00
|
|
|
|
Handle layout updates during LayoutAnimation animations on Android
Summary:
On Android, LayoutAnimation directly updates the layout since a generic
scaling animation is more difficult to implement. This causes a problem
if the layout is updated during an animation, as the previous layout is
stored with the animation and is not updated. As a result the view gets
the old layout instead once the animation completes.
This commit fixes this issue by storing the layout handling animations
while those animations are active, and updating the animations on the
fly if one of the views receives a new layout. The resulting behaviour
mirrors what iOS currently does.
This bug has real world consequences, for example if a LayoutAnimation
happens right after a VirtualizedList has mounted, it’s possible that
some list rows are mounted while the animation is active, making the
list content view bigger. If the content view is being animated, the
new size will not take effect and it becomes impossible to scroll to
the end of the list.
I wrote a minimal test case to verify the bug, which I’ve also added to
RNTester. You can find the standalone app here:
<https://gist.github.com/lnikkila/18096c15b2fb99b232795ef59f8fb0cd>
The app creates a 100x300 view that gets animated to 200x300 using
LayoutAnimation. In the middle of that animation, the view’s dimensions
are updated to 300x300.
The expected result (which is currently exhibited by iOS) is that the
view’s dimensions after the animation would be 300x300. On Android the
view keeps the 200x300 dimensions since the animation overrides the
layout update.
The test app could probably be turned into an integration test by
measuring the view through UIManager after the animation, however I
don’t have time to do that right now...
Here are some GIFs to compare, click to expand:
<details>
<summary><b>Current master (iOS vs Android)</b></summary>
<p></p>
<img src="https://user-images.githubusercontent.com/1291143/38191325-f1aeb3d4-3670-11e8-8aca-14e7b24e2946.gif" height="400" /><img src="https://user-images.githubusercontent.com/1291143/38191337-f643fd8c-3670-11e8-9aac-531a32cc0a67.gif" height="400" />
</details><p></p>
<details>
<summary><b>With this patch (iOS vs Android, fixed)</b></summary>
<p></p>
<img src="https://user-images.githubusercontent.com/1291143/38191325-f1aeb3d4-3670-11e8-8aca-14e7b24e2946.gif" height="400" /><img src="https://user-images.githubusercontent.com/1291143/38191355-07f6e972-3671-11e8-8ad2-130d06d0d64d.gif" height="400" />
</details><p></p>
No documentation changes needed.
[ANDROID] [BUGFIX] [LayoutAnimation] - View layout is updated correctly during an ongoing LayoutAnimation, mirroring iOS behaviour.
Closes https://github.com/facebook/react-native/pull/18651
Differential Revision: D7604698
Pulled By: hramos
fbshipit-source-id: 4d114682fd540419b7447e999910e05726f42b39
2018-04-12 20:44:45 +00:00
|
|
|
class LayoutUpdateExample extends React.Component<{}, $FlowFixMeState> {
|
|
|
|
state = {
|
|
|
|
width: 200,
|
|
|
|
height: 100,
|
|
|
|
};
|
|
|
|
|
|
|
|
timeout = null;
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
this._clearTimeout();
|
|
|
|
}
|
|
|
|
|
|
|
|
_clearTimeout = () => {
|
|
|
|
if (this.timeout !== null) {
|
|
|
|
clearTimeout(this.timeout);
|
|
|
|
this.timeout = null;
|
|
|
|
}
|
2018-05-11 20:32:37 +00:00
|
|
|
};
|
Handle layout updates during LayoutAnimation animations on Android
Summary:
On Android, LayoutAnimation directly updates the layout since a generic
scaling animation is more difficult to implement. This causes a problem
if the layout is updated during an animation, as the previous layout is
stored with the animation and is not updated. As a result the view gets
the old layout instead once the animation completes.
This commit fixes this issue by storing the layout handling animations
while those animations are active, and updating the animations on the
fly if one of the views receives a new layout. The resulting behaviour
mirrors what iOS currently does.
This bug has real world consequences, for example if a LayoutAnimation
happens right after a VirtualizedList has mounted, it’s possible that
some list rows are mounted while the animation is active, making the
list content view bigger. If the content view is being animated, the
new size will not take effect and it becomes impossible to scroll to
the end of the list.
I wrote a minimal test case to verify the bug, which I’ve also added to
RNTester. You can find the standalone app here:
<https://gist.github.com/lnikkila/18096c15b2fb99b232795ef59f8fb0cd>
The app creates a 100x300 view that gets animated to 200x300 using
LayoutAnimation. In the middle of that animation, the view’s dimensions
are updated to 300x300.
The expected result (which is currently exhibited by iOS) is that the
view’s dimensions after the animation would be 300x300. On Android the
view keeps the 200x300 dimensions since the animation overrides the
layout update.
The test app could probably be turned into an integration test by
measuring the view through UIManager after the animation, however I
don’t have time to do that right now...
Here are some GIFs to compare, click to expand:
<details>
<summary><b>Current master (iOS vs Android)</b></summary>
<p></p>
<img src="https://user-images.githubusercontent.com/1291143/38191325-f1aeb3d4-3670-11e8-8aca-14e7b24e2946.gif" height="400" /><img src="https://user-images.githubusercontent.com/1291143/38191337-f643fd8c-3670-11e8-9aac-531a32cc0a67.gif" height="400" />
</details><p></p>
<details>
<summary><b>With this patch (iOS vs Android, fixed)</b></summary>
<p></p>
<img src="https://user-images.githubusercontent.com/1291143/38191325-f1aeb3d4-3670-11e8-8aca-14e7b24e2946.gif" height="400" /><img src="https://user-images.githubusercontent.com/1291143/38191355-07f6e972-3671-11e8-8ad2-130d06d0d64d.gif" height="400" />
</details><p></p>
No documentation changes needed.
[ANDROID] [BUGFIX] [LayoutAnimation] - View layout is updated correctly during an ongoing LayoutAnimation, mirroring iOS behaviour.
Closes https://github.com/facebook/react-native/pull/18651
Differential Revision: D7604698
Pulled By: hramos
fbshipit-source-id: 4d114682fd540419b7447e999910e05726f42b39
2018-04-12 20:44:45 +00:00
|
|
|
|
|
|
|
_onPressToggle = () => {
|
|
|
|
this._clearTimeout();
|
|
|
|
this.setState({width: 150});
|
|
|
|
|
|
|
|
LayoutAnimation.configureNext({
|
|
|
|
duration: 1000,
|
|
|
|
update: {
|
|
|
|
type: LayoutAnimation.Types.linear,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
this.timeout = setTimeout(() => this.setState({width: 100}), 500);
|
|
|
|
};
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const {width, height} = this.state;
|
|
|
|
|
|
|
|
return (
|
|
|
|
<View style={styles.container}>
|
|
|
|
<TouchableOpacity onPress={this._onPressToggle}>
|
|
|
|
<View style={styles.button}>
|
|
|
|
<Text>Make box square</Text>
|
|
|
|
</View>
|
|
|
|
</TouchableOpacity>
|
|
|
|
<View style={[styles.view, {width, height}]}>
|
2018-05-11 20:32:37 +00:00
|
|
|
<Text>
|
|
|
|
{width}x{height}
|
|
|
|
</Text>
|
Handle layout updates during LayoutAnimation animations on Android
Summary:
On Android, LayoutAnimation directly updates the layout since a generic
scaling animation is more difficult to implement. This causes a problem
if the layout is updated during an animation, as the previous layout is
stored with the animation and is not updated. As a result the view gets
the old layout instead once the animation completes.
This commit fixes this issue by storing the layout handling animations
while those animations are active, and updating the animations on the
fly if one of the views receives a new layout. The resulting behaviour
mirrors what iOS currently does.
This bug has real world consequences, for example if a LayoutAnimation
happens right after a VirtualizedList has mounted, it’s possible that
some list rows are mounted while the animation is active, making the
list content view bigger. If the content view is being animated, the
new size will not take effect and it becomes impossible to scroll to
the end of the list.
I wrote a minimal test case to verify the bug, which I’ve also added to
RNTester. You can find the standalone app here:
<https://gist.github.com/lnikkila/18096c15b2fb99b232795ef59f8fb0cd>
The app creates a 100x300 view that gets animated to 200x300 using
LayoutAnimation. In the middle of that animation, the view’s dimensions
are updated to 300x300.
The expected result (which is currently exhibited by iOS) is that the
view’s dimensions after the animation would be 300x300. On Android the
view keeps the 200x300 dimensions since the animation overrides the
layout update.
The test app could probably be turned into an integration test by
measuring the view through UIManager after the animation, however I
don’t have time to do that right now...
Here are some GIFs to compare, click to expand:
<details>
<summary><b>Current master (iOS vs Android)</b></summary>
<p></p>
<img src="https://user-images.githubusercontent.com/1291143/38191325-f1aeb3d4-3670-11e8-8aca-14e7b24e2946.gif" height="400" /><img src="https://user-images.githubusercontent.com/1291143/38191337-f643fd8c-3670-11e8-9aac-531a32cc0a67.gif" height="400" />
</details><p></p>
<details>
<summary><b>With this patch (iOS vs Android, fixed)</b></summary>
<p></p>
<img src="https://user-images.githubusercontent.com/1291143/38191325-f1aeb3d4-3670-11e8-8aca-14e7b24e2946.gif" height="400" /><img src="https://user-images.githubusercontent.com/1291143/38191355-07f6e972-3671-11e8-8ad2-130d06d0d64d.gif" height="400" />
</details><p></p>
No documentation changes needed.
[ANDROID] [BUGFIX] [LayoutAnimation] - View layout is updated correctly during an ongoing LayoutAnimation, mirroring iOS behaviour.
Closes https://github.com/facebook/react-native/pull/18651
Differential Revision: D7604698
Pulled By: hramos
fbshipit-source-id: 4d114682fd540419b7447e999910e05726f42b39
2018-04-12 20:44:45 +00:00
|
|
|
</View>
|
|
|
|
</View>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-25 07:08:42 +00:00
|
|
|
const styles = StyleSheet.create({
|
|
|
|
container: {
|
|
|
|
flex: 1,
|
|
|
|
},
|
|
|
|
button: {
|
|
|
|
borderRadius: 5,
|
|
|
|
backgroundColor: '#eeeeee',
|
|
|
|
padding: 10,
|
|
|
|
marginBottom: 10,
|
|
|
|
},
|
|
|
|
buttonText: {
|
|
|
|
fontSize: 16,
|
|
|
|
},
|
|
|
|
viewContainer: {
|
|
|
|
flex: 1,
|
|
|
|
flexDirection: 'row',
|
|
|
|
flexWrap: 'wrap',
|
|
|
|
},
|
|
|
|
view: {
|
|
|
|
height: 54,
|
|
|
|
width: 54,
|
|
|
|
backgroundColor: 'red',
|
|
|
|
margin: 8,
|
|
|
|
alignItems: 'center',
|
|
|
|
justifyContent: 'center',
|
|
|
|
},
|
|
|
|
greenSquare: {
|
|
|
|
width: 150,
|
|
|
|
height: 150,
|
|
|
|
backgroundColor: 'green',
|
|
|
|
alignItems: 'center',
|
|
|
|
justifyContent: 'center',
|
|
|
|
},
|
|
|
|
blueSquare: {
|
|
|
|
width: 150,
|
|
|
|
height: 150,
|
|
|
|
backgroundColor: 'blue',
|
|
|
|
alignItems: 'center',
|
|
|
|
justifyContent: 'center',
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
exports.title = 'Layout Animation';
|
|
|
|
exports.description = 'Layout animation';
|
2018-05-11 20:32:37 +00:00
|
|
|
exports.examples = [
|
|
|
|
{
|
|
|
|
title: 'Add and remove views',
|
|
|
|
render(): React.Element<any> {
|
|
|
|
return <AddRemoveExample />;
|
|
|
|
},
|
2016-04-25 07:08:42 +00:00
|
|
|
},
|
2018-05-11 20:32:37 +00:00
|
|
|
{
|
|
|
|
title: 'Cross fade views',
|
|
|
|
render(): React.Element<any> {
|
|
|
|
return <CrossFadeExample />;
|
|
|
|
},
|
2016-04-25 07:08:42 +00:00
|
|
|
},
|
2018-05-11 20:32:37 +00:00
|
|
|
{
|
|
|
|
title: 'Layout update during animation',
|
|
|
|
render(): React.Element<any> {
|
|
|
|
return <LayoutUpdateExample />;
|
|
|
|
},
|
Handle layout updates during LayoutAnimation animations on Android
Summary:
On Android, LayoutAnimation directly updates the layout since a generic
scaling animation is more difficult to implement. This causes a problem
if the layout is updated during an animation, as the previous layout is
stored with the animation and is not updated. As a result the view gets
the old layout instead once the animation completes.
This commit fixes this issue by storing the layout handling animations
while those animations are active, and updating the animations on the
fly if one of the views receives a new layout. The resulting behaviour
mirrors what iOS currently does.
This bug has real world consequences, for example if a LayoutAnimation
happens right after a VirtualizedList has mounted, it’s possible that
some list rows are mounted while the animation is active, making the
list content view bigger. If the content view is being animated, the
new size will not take effect and it becomes impossible to scroll to
the end of the list.
I wrote a minimal test case to verify the bug, which I’ve also added to
RNTester. You can find the standalone app here:
<https://gist.github.com/lnikkila/18096c15b2fb99b232795ef59f8fb0cd>
The app creates a 100x300 view that gets animated to 200x300 using
LayoutAnimation. In the middle of that animation, the view’s dimensions
are updated to 300x300.
The expected result (which is currently exhibited by iOS) is that the
view’s dimensions after the animation would be 300x300. On Android the
view keeps the 200x300 dimensions since the animation overrides the
layout update.
The test app could probably be turned into an integration test by
measuring the view through UIManager after the animation, however I
don’t have time to do that right now...
Here are some GIFs to compare, click to expand:
<details>
<summary><b>Current master (iOS vs Android)</b></summary>
<p></p>
<img src="https://user-images.githubusercontent.com/1291143/38191325-f1aeb3d4-3670-11e8-8aca-14e7b24e2946.gif" height="400" /><img src="https://user-images.githubusercontent.com/1291143/38191337-f643fd8c-3670-11e8-9aac-531a32cc0a67.gif" height="400" />
</details><p></p>
<details>
<summary><b>With this patch (iOS vs Android, fixed)</b></summary>
<p></p>
<img src="https://user-images.githubusercontent.com/1291143/38191325-f1aeb3d4-3670-11e8-8aca-14e7b24e2946.gif" height="400" /><img src="https://user-images.githubusercontent.com/1291143/38191355-07f6e972-3671-11e8-8ad2-130d06d0d64d.gif" height="400" />
</details><p></p>
No documentation changes needed.
[ANDROID] [BUGFIX] [LayoutAnimation] - View layout is updated correctly during an ongoing LayoutAnimation, mirroring iOS behaviour.
Closes https://github.com/facebook/react-native/pull/18651
Differential Revision: D7604698
Pulled By: hramos
fbshipit-source-id: 4d114682fd540419b7447e999910e05726f42b39
2018-04-12 20:44:45 +00:00
|
|
|
},
|
2018-05-11 20:32:37 +00:00
|
|
|
];
|