react-native-ui-kitten icon indicating copy to clipboard operation
react-native-ui-kitten copied to clipboard

Configurable animations

Open mdebo opened this issue 4 years ago • 14 comments

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!

mdebo avatar Jan 20 '20 09:01 mdebo

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

artyorsh avatar Jan 20 '20 10:01 artyorsh

OK thanks - yes, i'll leave it open

mdebo avatar Jan 20 '20 10:01 mdebo

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 avatar Jan 24 '20 04:01 lesmo

@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 :)

artyorsh avatar Jan 24 '20 07:01 artyorsh

Yes! I'm very interested in having animated modals / popups / tooltips!!

annie-elequin avatar Feb 07 '20 18:02 annie-elequin

I'm going to look into the Animatable library though, I haven't seen that.

annie-elequin avatar Feb 07 '20 20:02 annie-elequin

@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.

mdebo avatar Feb 14 '20 08:02 mdebo

i'd love to animated the TabView component as the easing / animation is too slow in my opinion

fr3fou avatar Feb 17 '20 13:02 fr3fou

In the meantime, anyone have any examples of an animated Modal they could share? Thanks!

m4ttheweric avatar Mar 30 '20 19:03 m4ttheweric

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 :)

CostachescuCristinel avatar Jul 04 '20 00:07 CostachescuCristinel

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]}
    />
  );
};

sschottler avatar Sep 14 '20 18:09 sschottler

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>
  );
};

sschottler avatar Sep 14 '20 18:09 sschottler

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],
  };
}

sschottler avatar May 24 '21 15:05 sschottler

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.

greenafrican avatar Mar 22 '22 10:03 greenafrican