0

I'm trying to create a button that resizes (gets a little bit smaller when it' pressed). I use TouchableWithoutFeedback from react-native-gesture-handlerand I use react-native-reanimated.

This is my code so far:

import React, { useState } from 'react';
import { View } from 'react-native';
import Animated, { Easing, Extrapolate } from 'react-native-reanimated';
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';

const { interpolate, sub } = Animated;

const TouchableResize = (props) => {
  const { onPress, children } = props;
  const [scale, setScale] = useState(0);
  const scaling = interpolate(scale, {
    inputRange: [0, 1],
    outputRange: [1, 0.90],
    extrapolate: Extrapolate.CLAMP
  });
  return (
    <TouchableWithoutFeedback onPressIn={() => setScale(1)} onPressOut={() => setScale(0)}>
      <Animated.View style={{ transform: [{ scaleX: scaling }, { scaleY: scaling }] }}>
        {children}
      </Animated.View>
    </TouchableWithoutFeedback>
  );
};

export { TouchableResize };

This code works partly. The button resizes to 0.90 when it's pressed, but the animation is not smooth. It snaps directly to 0.90, and when it's released, the button directly snaps back.

How can I update my code so the animation runs smoothly? Please note I'm a complete beginner in react-native-reanimated.

Sam Leurs
  • 1,592
  • 1
  • 19
  • 48

1 Answers1

3

You have to use timing function to change your Animated.Value over time. Here example in docs. Also, I created expo snack example. Here updated component code

import React, { useState, useMemo } from 'react';
import { View } from 'react-native';
import Animated, { Easing, Extrapolate } from 'react-native-reanimated';
import { TouchableWithoutFeedback } from 'react-native-gesture-handler';

const {
  Clock,
  Value,
  set,
  cond,
  startClock,
  clockRunning,
  timing,
  debug,
  stopClock,
  block,
  interpolate,
  useCode,
} = Animated;

function runTiming(clock, from, to) {
  const state = {
    finished: new Value(0),
    position: new Value(from),
    time: new Value(0),
    frameTime: new Value(0),
  };

  const config = {
    duration: 100,
    toValue: new Value(to),
    easing: Easing.inOut(Easing.ease),
  };

  return block([
    cond(
      clockRunning(clock),
      [],
      startClock(clock),
    ),
    // we run the step here that is going to update position
    timing(clock, state, config),
    // if the animation is over we stop the clock
    cond(state.finished, debug('stop clock', stopClock(clock))),
    // we made the block return the updated position
    state.position,
  ]);
}


const TouchableResize = (props) => {
  const { onPress, children } = props;
  const [pressed, setPressed] = useState(false);
  const {clock, scale} = useMemo(() => ({
    clock: new Clock(),
    scale: new Value(1), 
  }), [])

  useCode(
    () => block([
      pressed ? set(scale, runTiming(clock, 0, 1)) : set(scale, runTiming(clock, 1, 0))
    ]), [pressed]
  );

  const scaling = interpolate(scale, {
    inputRange: [0, 1],
    outputRange: [1, 0.90],
    extrapolate: Extrapolate.CLAMP
  });
  return (
    <TouchableWithoutFeedback onPressIn={() => setPressed(true)} onPressOut={() => setPressed(false)}>
      <Animated.View style={{ transform: [{ scaleX: scaling }, { scaleY: scaling }] }}>
        {children}
      </Animated.View>
    </TouchableWithoutFeedback>
  );
};

export { TouchableResize };
Dima Portenko
  • 3,443
  • 5
  • 35
  • 48