cloudbit-interactive / GSAP-RN

MIT License
47 stars 4 forks source link

{width: null} will not unset the width property #6

Closed cgo123 closed 2 years ago

cgo123 commented 2 years ago

{width: null} will not unset the width property, it works like "0".

If you set an element to Width 100% and later want to set it back to Width "auto", then unfortunately this is currently not possible.

tufik2 commented 2 years ago

Hi, width: null should not be used, but width:'auto' is supported.. do you have an example code to evaluate the issue more in detail?.. If you want to make a tween between 100% to auto you will have to calculate 'auto' in % before making the tween and after tween end set the property to 'auto'

I leave this code making a tween between 10% =>tween=> 100% =>set=> auto

import React from 'react';
import {Pressable, StyleSheet, Text, View} from 'react-native';
import {AutoKillTweens, Elastic, gsap} from 'gsap-rn';

export const SimpleAnimation = () => {
  const boxRef = React.createRef();
  const tweens = {};

  const animate = () => {
    tweens.ani1 = gsap.timeline();
    tweens.ani1.set(boxRef.current, {style: {width: '10%'}});
    tweens.ani1.to(boxRef.current, { duration: 1, style: {width: '80%'}, ease: Elastic.easeInOut,});
    tweens.ani1.set(boxRef.current, {style: {width: 'auto'}});
  };

  return (
    <>
      <AutoKillTweens tweens={tweens} />
      <View ref={boxRef} style={[style.box]} />
      <Pressable onPress={animate} style={[style.button, {marginTop: 20}]}>
        <Text>Click</Text>
      </Pressable>
    </>
  );
};

export const style = StyleSheet.create({
  box: {
    padding: 20,
    width: '50%',
    height: 100,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'red',
  },
  button: {
    padding: 20,
    justifyContent: 'center',
    alignItems: 'center',
    width: 'auto',
    backgroundColor: '#dddddd',
  },
});
tufik2 commented 2 years ago

Yes, in react-native you can’t mix % with Numbers, if you will do an animation with % just use % value, or if your animation is between 2 Numbers only use numbers, Also, you need to set the initial value before starting the animation, due in ReactNative there is no way to retrieve the current values.

You can use gsap.fromTo(... or gsap.set(... before start your animation, after finish you can use gsap.set to reset value

Some examples

const animate = () => {
    AutoKillTweens.kill(tweens);
    tweens.ani1 = gsap.timeline();
    // set width to 0% and animate to 100%
    tweens.ani1.fromTo(boxRef.current, {style:{width:'0%'}}, {duration: 1, style: {width: '100%'}});
    // set width to Number (Dimension screen) and animate to 300
    tweens.ani1.fromTo(boxRef.current, {style: {width: Dimensions.get('window').width}}, {duration: 1, style: {width: 300}});
    // another way to set values before an animation is using .set(), in this case we are resting width to auto
    tweens.ani1.set(boxRef.current, {style: {width: 'auto'}});
    // set width to 50% before starting next animation
    tweens.ani1.set(boxRef.current, {style: {width: '50%'}, delay:1});
    // animate to width of 90%
    tweens.ani1.to(boxRef.current, {duration:1, style: {width: '90%'}, delay:1});
    // reset width to auto
    tweens.ani1.set(boxRef.current, {style: {width: 'auto'}});
    // NO POSSIBLE: This will not animate because 'auto' is not a real value, so we have to figure out what auto meaning to perform the animation
    tweens.ani1.fromTo(boxRef.current, {style:{width:100}}, {style: {width: 'auto'}});
  };
cgo123 commented 2 years ago

I have found a way to animate the height without having to give a fixed value. This has the advantage that the animation does not jump!

    const [bodyHeight, setBodyHeight] = useState(0);

    useLayoutEffect(() => {
        checkBodyHeight();
    }, []);

    const checkBodyHeight = () => {
        setTimeout(() => {
            animationViewRef.current?.measure((x, y, width, height, pageX, pageY) => {
                if (height > 0) {
                    setBodyHeight(height);
                }
            })
        }, 0)
    }

    const playOpenAnimation = () => {
        animationTimeLine.set(animationViewRef.current, {
            style: {
                height: 0,
            },
        });

        animationTimeLine.to(animationViewRef.current, {
            style: {
                height: bodyHeight,
                opacity: 1
            },
            duration: 1,
            ease: Power4.easeOut,
        })

        animationTimeLine.set(animationViewRef.current, {
            style: {
                height: 'auto'
            },
            onComplete: () => {
                checkBodyHeight();
            }
        });
    }

    const playCloseAnimation = () => {
        animationTimeLine.set(animationViewRef.current, {
            style: {
                height: bodyHeight,
            },
        });

        animationTimeLine.to(animationViewRef.current, {
            style: {
                height: 0,
                opacity: 0
            },
            duration: 1,
            ease: Power4.easeOut,
        })
    }