Poyán
En react-native, ¿cómo animas el tamaño de una vista cuando no sabes el tamaño de su contenido?
Digamos que la altura del contenido de la Vista puede estar entre 0 y 400 puntos, y el contenido puede crecer y reducirse dinámicamente, y desea animar cualquier cambio en la altura.
Básicamente, estoy buscando reproducir el comportamiento de LayoutAnimation sin usar LayoutAnimation pero usando Animado.
Creo que lo que se me escapa es que no sé cómo animar hacia una altura de destino, ya que no sé la altura del contenido.
yo suelo LayoutAnimation
para eso, justo antes del cambio de estado que hace que cambie la altura de su componente, agregue:
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
Puede utilizar diferentes ajustes preestablecidos:
- primavera
- Facilidad de entradaFacilidad de salida
-
lineal
Puedes leer más sobre esto aquí https://facebook.github.io/react-native/docs/layoutanimation.html
-
LayoutAnimation establecerá una animación global en todo, este método es solo si tiene una aplicación muy simple en la que se lleva a cabo 1 acción a la vez.
– ericjam
8 jun 2018 a las 22:32
-
LayoutAnimation todavía tiene muchos problemas fatales tanto en iOS como en Android. Si está considerando admitir varios dispositivos móviles, sugiero API animada todo el tiempo.
– Samitha Nanayakkara
3 de abril de 2020 a las 9:32
-
LayoutAnimation tiene muchos errores en Android y ha provocado varios bloqueos en la producción de mis aplicaciones. Tuve que reemplazarlo (es decir
Platform.OS === 'ios' ? LayoutAnimation.configureNext(LayoutAnimation.create(50, 'easeInEaseOut', 'opacity')) : null;
) y reemplácelo con llamadas solo de iOS.–Walter Monecke
21 de abril de 2020 a las 9:10
-
He hecho una respuesta a continuación para cualquier persona que experimente una situación similar a la mía en el manejo de la animación. Podria manejar la altura del contenido y usando
Animated
componente. Siéntete libre de echar un vistazo ;D ¡Saludos!– Ala Choy
12 de noviembre de 2020 a las 4:45
ala choy
Hola a todos, espero que no sea demasiado tarde…
para cualquiera que esté lidiando con la animación React Native en la altura de la vista.
Sé que es muy molesto como:
✖️ React Native
animación parece no admite estilos de diseño (por ejemplo, ancho y alto)
✖️ LayoutAnimation
parece complicado de investigar
✖️ Desea usar una forma oficial de animar en lugar de instalar un paquete de terceros
✖️ A veces, el contenido puede ser grande para romper sus estilos de vista
así que aquí está mi solución para ti (forma de componente de clase):
Primero, establezca el valor animado en el estado:
state = { height: new Animated.Value(0) };
A continuación, configure su vista animada max height
con interpolación de animación:
const maxHeight = this.state.height.interpolate({
inputRange: [0, 1],
outputRange: [0, 2000] // <-- any value larger than your content's height
});
return (<Animated.View style={[styles.box, { maxHeight: maxHeight }]} />);
// any other fixed styles in styles.box
Después de eso, configura la animación dentro del function
usted llamó,
o componentDidMount
si desea que se muestre tan pronto como se represente:
// or in any function that users interact
componentDidMount() {
Animated.timing(this.state.formHeight, {
toValue: 1,
duration: 500, // <-- animation duration
easing: Easing.linear, // <-- or any easing function
useNativeDriver: false // <-- need to set false to prevent yellow box warning
}).start();
}
Sé consciente de no establezca useNativeDriver
a true
ya que no es compatible con los estilos de diseño.
Muestra
Así que a continuación hay una muestra para que interactúes,
no dude en copiar y pegar para usted React Native
proyecto para probar:
import React, { PureComponent } from 'react';
import { Animated, Button, Easing, View, Text, StyleSheet } from 'react-native';
class AnimateBox extends PureComponent {
state = { opacity: new Animated.Value(0), height: new Animated.Value(0) };
showContent = () => {
const { opacity, height } = this.state;
Animated.timing(height, {
toValue: 1,
duration: 500,
easing: Easing.linear,
useNativeDriver: false // <-- neccessary
}).start(() => {
Animated.timing(opacity, {
toValue: 1,
duration: 500,
easing: Easing.linear,
useNativeDriver: false // <-- neccessary
}).start();
});
};
render() {
const { opacity, height } = this.state;
const maxHeight = height.interpolate({
inputRange: [0, 1],
outputRange: [0, 1000] // <-- value that larger than your content's height
});
return (
<View style={styles.box}>
<Animated.View style={{ opacity: opacity, maxHeight: maxHeight }}>
<Text style={styles.content}>
Lorem Ipsum is simply a dummy text of the printing and typesetting industry.
Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,
when an unknown printer took a galley of type and scrambled it to make a type specimen book.
It has survived not only five centuries, but also the leap into electronic typesetting,
remaining essentially unchanged. It was popularised in the 1960s with the release of
Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
</Text>
</Animated.View>
<View style={styles.spacing}>
<Button title="Show content" onPress={this.showContent} />
</View>
</View>
);
}
}
const styles = StyleSheet.create({
box: {
backgroundColor: '#fff',
marginHorizontal: 15,
paddingHorizontal: 15
},
spacing: {
paddingVertical: 10
},
content: {
fontSize: 16,
lineHeight: 30,
color: '#555'
}
});
export default AnimateBox;
Codificación feliz 🙂
Más explicación (Editar el 03 de febrero de 2021)
Como se menciona en la documentación de React Native:
https://reactnative.dev/docs/animations#advertencias
No todo lo que puede hacer con Animated actualmente es compatible con el controlador nativo. La principal limitación es que solo puede animar propiedades que no sean de diseño: cosas como la transformación y la opacidad funcionarán, pero las propiedades de posición y Flexbox no.
Deshabilitar useNativeDriver
puede animar estilos que no sean opacity
y transform
pero de hecho aumenta la carga de trabajo en el subproceso JS ya que el subproceso JS necesita calcular la interfaz de usuario en cada cuadro.
Y aquí en el blog de React Native 0.62:
https://reactnative.dev/blog/2020/03/26/version-0.62#deprecaciones
Ahora es necesario configurar useNativeDriver para poder cambiar el valor predeterminado en el futuro.
Debes tener que configurar el useNativeDriver
en opciones de animación en React native v0.62
o superior para evitar cualquier advertencia.
-
¿De verdad? por qué dice “La propiedad de estilo ‘altura’ no es compatible con el módulo animado nativo” para mí. quiero llorar
– Charitha Goonewardena
3 de febrero de 2021 a las 2:36
-
Hola @CharithaGoonewardena, creo que es posible que hayas configurado el
useNativeDriver
a verdadero, lo que conduce a esa advertencia. He agregado alguna explicación a la respuesta. No dude en echar un vistazo o copiar el código de muestra en su proyecto para editarlo. ¡Salud!– Ala Choy
3 de febrero de 2021 a las 7:35
-
Esto funciona, pero realmente no es necesario interpolar. Simplemente puede animar a 2000 en lugar de 1 y establecer maxHeight en this.state.formHeight
– John Kendall
19 de febrero de 2021 a las 15:54
-
Sí, por supuesto, @Johnkendall 🙂 La interpolación solo se usa para manejar maxHeight y opacidad con el mismo valor animado en mi muestra. Depende de usted definir el valor máximo si no tiene otros estilos animados.
– Ala Choy
21 de febrero de 2021 a las 6:44
-
En la animación de Android no hay transición en absoluto. Es simplemente ir a valorar directamente. ¿Alguien se encontró con esto? iOS funciona bien
– Necmettin Sargin
13 de julio de 2021 a las 9:38
Tierna D. Jack Lawless
Tendrá que agregar algún tipo de escala de tamaño, probablemente con porcentajes para obtener el mejor efecto.
Lo primero es lo primero que necesitarías usar Animated.View
en vez de View
.
A continuación, deberá aplicar una transformación al estilo de la vista, digamos que se ve a continuación. Esta es la parte que actualiza, cambia y crea el movimiento.
<Animated.View style={[ styles.someStyleYouMade,
{
transform: [
// scaleX, scaleY, scale, theres plenty more options you can find online for this.
{ scaleX: ViewScaleValue } // this would be the result of the animation code below and is just a number.
]
}
]}
>
La siguiente parte es básicamente un ejemplo de API animada, escribirías algo como esto (personalizado como quieras) y cuando se llame a este script, se animará de la forma que especifiques.
Animated.timing( // Animate over time
this.state.ViewScale, // The animated value to drive, this would be a new Animated.Value(0) object.
{
toValue: 100, // Animate the value
duration: 5000, // Make it take a while
}
).start();
Por último, probablemente querrá aplicar una interpolación al valor para que parezca lo más personalizado posible.
(esto entrará en su render()
función pero antes de la return()
. la ViewScaleValue
entrará en el Animated.View
transformar)
const ViewScaleValue = this.state.ViewScale.interpolate({
inputRange: [0, 25, 50, 75, 100],
outputRange: [0, .5, 0.75, 0.9, 1]
});
todo este código haría ViewScaleValue
un número simple, anime de 0 a 100, vaya rápido y luego lento (debido a la interpolación) y aplique cada iteración de la animación al Animated.View
transformar.
Lea la API animada junto con esto para comprenderlo bien.
-
Usando este método, ¿cómo puedo ajustar la altura sin escalar? Parece que solo hay opciones de escala disponibles.
– CyberMew
14 de marzo de 2019 a las 14:33
-
cuando la vista animada tiene valor cero, entonces es simplemente invisible … aún así, está tomando el lugar que requerirá cuando el contenido sea visible
– Samiksha Jagtap
16 oct 2019 a las 6:49
-
Por qué %? ¿Puede explicar cómo el porcentaje ayudará a OP a lograr su objetivo?
– png
30 de agosto de 2021 a las 21:38
sbearben
El método que he tomado es gastar pases de diseño para obtener la altura del componente “truncado” y la altura del componente de “tamaño completo” (necesita una forma de que la altura truncada sea determinista, generalmente sabiendo cómo renderizar un “fila” de contenido). Esencialmente, antes de tener esos valores, los representa como dos vistas separadas que están ocultas:
hidden: {
position: 'absolute',
left: 0,
top: 0,
opacity: 0,
},
Usa onLayout para capturar sus alturas:
const onLayoutTruncated = ({nativeEvent}: LayoutChangeEvent) => {
if (!doneProcessing) {
truncatedHeight = nativeEvent.layout.height;
checkIfDoneProcessingLayout();
}
};
const onLayoutFull = ({nativeEvent}: LayoutChangeEvent) => {
if (!doneProcessing) {
fullHeight = nativeEvent.layout.height;
checkIfDoneProcessingLayout();
}
};
checkIfDoneProcessingLayout()
comprobará si ambos truncatedHeight
y fullHeight
están configurados y hacen un cambio de estado si ambos lo están (doneProcessing = true).
Desde allí, debe mostrar la vista truncada y poder animar entre ambos valores de altura usando un Animated.Value
e interpolación:
const expandingHeight = animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [truncatedHeight, fullHeight],
});
Active la animación de expansión/contracción al hacer clic usando Animated.timing
Animated.timing(animatedValue, {toValue: isExpanded ? 0 : 1, easing: SOME_EASING_FUNCTION, duration: SOME_DURATION}).start();
Yo uso propio componente con @react-spring/native
algo como esto
import React from 'react';
import {View, ViewProps} from 'react-native';
interface GetDimensionsOfThisContainerProps extends ViewProps {
children: React.ReactChild[];
onDimensions: ({width, height}: {width: Number; height: Number}) => void;
}
export const GetDimensionsOfThisContainer: React.FC<
GetDimensionsOfThisContainerProps
> = ({children, onDimensions, ...props}: GetDimensionsOfThisContainerProps) => {
return (
<View
onLayout={event =>
onDimensions({
width: Math.round(event.nativeEvent.layout.width),
height: Math.round(event.nativeEvent.layout.height),
})
}
style={{position: 'absolute', width: '100%'}}
{...props}>
{children}
</View>
);
};
<GetDimensionsOfThisContainer
onDimensions={({height: _height}) => {
if (dynamicHeight !== _height) setDynamicHeight(_height);
}}>
{children}
</GetDimensionsOfThisContainer>
fuente: https://gist.github.com/sturmenta/af790331f6bd27322ecd73ee723c8c60
Joachim Feltkamp
Basado en la solución de @Wing Choi, quiero compartir mi solución mejorada. Esto debería funcionar plug & play.
- En lugar de usar maxHeight (no tan bueno para las animaciones), usé onLayout-Event para tener siempre la altura exacta. (Se disparará en todos los cambios internos y externos de la capa)
- En lugar de simplemente abrir la capa con un botón, agregué una propiedad “abrir” (bool) para controlar la capa desde un componente principal.
import React, { PureComponent } from 'react';
import { Animated, Easing, View } from 'react-native';
import PropTypes from "prop-types";
class AnimateBox extends PureComponent {
constructor(props) {
super(props);
this.state = {
init: false,
opacity: (this.props.open) ? 1 : 0,
height: 1000
};
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (this.state.init && prevState.open !== this.props.open) {
this.showContent();
}
}
adjustHeight(layout) {
const height = Math.round(layout.height);
this.setState({
init: true,
opacity: new Animated.Value((this.props.open) ? 1 : 0),
height: new Animated.Value((this.props.open) ? height : 0),
interpol: {
inputRange: [0, 1],
outputRange: [0, height]
}
});
}
showContent = () => {
const { opacity, height } = this.state;
Animated.timing(height, {
toValue: (this.props.open) ? 1 : 0,
duration: 400,
easing: Easing.out(Easing.ease),
useNativeDriver: false
}).start(() => {
Animated.timing(opacity, {
toValue: (this.props.open) ? 1 : 0,
duration: 300,
easing: Easing.linear,
useNativeDriver: false
}).start();
});
};
render() {
const { opacity, height, interpol } = this.state;
let animHeight = height;
if (this.state.init) {
animHeight = height.interpolate(interpol);
}
return (
<Animated.View style={{ position: 'relative', opacity: opacity, height: animHeight, overflow: 'hidden' }}>
<View style={{position: 'absolute', top: 0, left: 0, right: 0}}
onLayout={(event) => { this.adjustHeight(event.nativeEvent.layout)}} >
{this.props.children}
</View>
</Animated.View>);
}
}
AnimateBox.propTypes = {
open: PropTypes.bool
};
AnimateBox.defaultProps = {
open: false
}
export default AnimateBox;
animas usando valores de traducción, consulta stackoverflow.com/questions/55339044/…
– Yathavan
8 de enero de 2021 a las 6:13