Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configurable animations #837

Open
mdebo opened this issue Jan 20, 2020 · 14 comments
Open

Configurable animations #837

mdebo opened this issue Jan 20, 2020 · 14 comments
Labels
💡 Proposal 📱 Components components module-specific

Comments

@mdebo
Copy link

mdebo commented Jan 20, 2020

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!

@artyorsh
Copy link
Collaborator

artyorsh commented Jan 20, 2020

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

@mdebo
Copy link
Author

mdebo commented Jan 20, 2020

OK thanks - yes, i'll leave it open

@artyorsh artyorsh added 💡 Proposal 📱 Components components module-specific labels Jan 20, 2020
@artyorsh artyorsh changed the title Configure animation Configurable animations Jan 20, 2020
@lesmo
Copy link

lesmo commented Jan 24, 2020

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 🤔

@artyorsh
Copy link
Collaborator

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

@annie-elequin
Copy link

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

@annie-elequin
Copy link

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

@mdebo
Copy link
Author

mdebo commented Feb 14, 2020

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

@fr3fou
Copy link

fr3fou commented Feb 17, 2020

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

@m4ttheweric
Copy link

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

@CostachescuCristinel
Copy link

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

@sschottler
Copy link

sschottler commented Sep 14, 2020

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
Copy link

sschottler commented Sep 14, 2020

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
Copy link

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

@greenafrican
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
💡 Proposal 📱 Components components module-specific
Projects
None yet
Development

No branches or pull requests

9 participants