Open mdebo opened 4 years ago
No, we don't support configurable animations currently (even on the internal level), but it is on the plan for future releases. Let's keep this issue open to view the user interest and tracking a progress
OK thanks - yes, i'll leave it open
I think this should be out of scope for UI Kitten, there's some powerful and simple libraries like react-native-animatable that can be easily integrated with this library 🤔
@lesmo I guess there are some basic use-cases like animating modals that should be possible to customize, but currently there is no way to do that :)
Yes! I'm very interested in having animated modals / popups / tooltips!!
I'm going to look into the Animatable library though, I haven't seen that.
@lesmo it's not about the difficulty to implement it or not - according to me the purpose of using a framework for your UI is to avoid this and to preserve consistancy and homogeneity of UI and UX So i keep thinking it's fully in scope - I'll even say more: it's part of user experience. For exemple, angular material implementation in angular use a mat-ripple directive that is used to both indicate the point of touch, and to confirm that touch input was received.
i'd love to animated the TabView component as the easing / animation is too slow in my opinion
In the meantime, anyone have any examples of an animated Modal they could share? Thanks!
For modals, wrap your content in an Animated.View as first child. To start/stop animations on mount/unmount, you may create a wrapper class component where you can start and stop animations.
class CustomAnimatedModal extends React.Component {
constructor(props){
super(props);
// should start with invisible content
// Until componentDidMount is triggered, no content will be visible due to opacity=0
// when componentDidMount is triggered, content will fade in
this.opacityAnimation = new Animated.Value(0.0);
}
componentDidMount = ()=>{
Animated.timing(this.opacityAnimation, { toValue: 1.0, duration: 1000, useNativeDriver: true }).start();
}
componentWillUnmount = ()=>{
Animated.timing(this.opacityAnimation, { toValue: 0.0, duration: 1000, useNativeDriver: true }).start();
}
render = ()=>{
return <Modal visible={this.state.visible}>
<Animated.View style={{ width: "100%", height: "100%", opacity: this.opacityAnimation }}>
{/* content */}
</Animated.View>
</Modal>;
}
For other components, you can usually wrap them in Animated.View that you can then animate as you want.
<Layout level="1" style={{ flex: 1 }}>
<Animated.View style={{ opacity: this.opacityAnimation.interpolate({ inputRange: [0.0, 1.0], outputRange: [0.3, 0.8] }) }}>
<Layout level="2">{/* note how no flex/width/height styling is specified here, this Layout will take the size of the content - Animated.View - mostly behaving as one (except for position properties) */}
<Animated.View style={{ width: "100%", height: this.opacityAnimation.interpolate({ inputRange: [0.0, 1.0], outputRange: [50, 150] }) }}>
<Text category="c2">example text</Text>
</Animated.View>
</Layout>
</Animated.View>
</Layout>
Hopefully you'll find this useful :)
Here's one way to animate a button using composition pattern:
const AnimatedButton = ({ style, onPressIn, onPressOut, ...props }) => {
const animation = useRef(new Animated.Value(1)).current;
const handlePressIn = e => {
Animated.spring(animation, {
toValue: 0.5,
useNativeDriver: true,
}).start();
onPressIn && onPressIn(e);
};
const handlePressOut = e => {
Animated.spring(animation, {
toValue: 1,
friction: 3,
tension: 40,
useNativeDriver: true,
}).start();
onPressOut && onPressOut(e);
};
const animatedStyle = { transform: [{ scale: animation }] };
return (
<Button
onPressIn={handlePressIn}
onPressOut={handlePressOut}
{...props}
style={[style, animatedStyle]}
/>
);
};
If you wanted the Material ripple effect, you could use the react-native-material-ripple
package and plugin to the ui-kitten theme (and mapping if desired with the styled
hoc). Something like:
const AnimatedButton = ({children, ...props}) => {
const theme = useTheme();
return (
<Ripple
rippleColor="white"
rippleOpacity={1}
style={{backgroundColor: theme['color-primary-default'], height: 60}}
{...props}>
<Text>{children}</Text>
</Ripple>
);
};
Follow up on my button example. If you want to animate styles like backgroundColor
, you need to use Animated.createAnimatedComponent
and set useNativeDriver
to false:
import React, { FC } from 'react';
import { Animated } from 'react-native';
import { Button, ButtonProps } from '@ui-kitten/components';
import { useScaleAnimation } from './useScaleAnimation';
const AnimatableButton = Animated.createAnimatedComponent(Button);
export const AnimatedButton: FC<ButtonProps> = (props) => {
const animatedProps = useScaleAnimation(props);
return <AnimatableButton {...animatedProps} />;
};
/*
If you need animation to be theme-able, you could add
an "AnimatedButton" key to custom mapping with
custom parameters that control animation type, then access
those parameters from eva prop by wrapping this component with the
styled HOC like this: styled('AnimatedButton')(AnimatedButton)
*/
import { useRef } from 'react';
import { Animated, Easing, GestureResponderEvent } from 'react-native';
import { ButtonProps, useTheme } from '@ui-kitten/components';
const BUTTON_PRESSED_SIZE = 0.95;
const BUTTON_EASING = Easing.bezier(0.25, 0.1, 0.25, 1);
export function useScaleAnimation({
appearance = 'filled',
status = 'primary',
onPressIn,
onPressOut,
style,
...props
}: ButtonProps): ButtonProps {
const scale = useRef(new Animated.Value(1)).current;
const theme = useTheme();
const shrink = () => {
Animated.timing(scale, {
toValue: BUTTON_PRESSED_SIZE,
duration: 100,
easing: BUTTON_EASING,
useNativeDriver: false,
}).start();
};
const grow = () => {
Animated.timing(scale, {
toValue: 1,
duration: 300,
easing: BUTTON_EASING,
useNativeDriver: false,
}).start();
};
const handlePressIn = (e: GestureResponderEvent) => {
shrink();
onPressIn && onPressIn(e);
};
const handlePressOut = (e: GestureResponderEvent) => {
grow();
onPressOut && onPressOut(e);
};
const animatedStyle: any = { transform: [{ scale }] };
if (appearance === 'filled') {
const defaultColor = theme[`color-${status}-default`];
const activeColor = theme[`color-${status}-active`];
const backgroundColor = scale.interpolate({
inputRange: [BUTTON_PRESSED_SIZE, 1],
outputRange: [activeColor, defaultColor],
});
animatedStyle.backgroundColor = backgroundColor;
}
return {
status,
appearance,
...props,
onPressIn: handlePressIn,
onPressOut: handlePressOut,
style: [style, animatedStyle],
};
}
Another use case for this is to be able to control the animation ease curve when switching between Tabs in a TabView. I'd like to have some control of the speed an arrival ease as they feel quite abrupt.
Hi
I would like to have a way to configure animation using ui.kitten. For exemple when opening modal, select or menu etc. Is there a way to do it properly? (using the theme for exemple, to preserve consistancy accross all the app)?
Thanks!