The Airbnb iOS application has a small but very pleasant effect when you touch some of the cards that appear in the various result lists, when you touch them they get smaller and when you stop touching them they return to their original size.

The effect looks like this:

Personally I love that effect and that's why I decided to implement it in an application I did recently.

To achieve this effect I use React Native's Animated library and styled-components to give a bit of style to the cards.

The first thing you have to do is create a new file and give it a name, in my case I will call it Card.js, this will be the component that you will be able to reuse in the different parts of your application.Then it imports the necessary dependencies:

import React, { PureComponent } from 'react';
import { Dimensions, Animated, TouchableWithoutFeedback } from 'react-native';
import styled from 'styled-components';

// I use these values because I have two columns of cards with some space and because
// I want to keep a vertical ratio.
// You can change them for some fixed values or anything else, it depends of your needs
const cardWidth = (Dimensions.get('window').width / 2) - 30;
const cardHeight = cardWidth * 1.4

Now we have to bind a couple of functions in the constructor:

class Card extends PureComponent {
  constructor(props) {
    super(props);

    this.handlePressIn = this.handlePressIn.bind(this);
    this.handlePressOut = this.handlePressOut.bind(this);
  }
  ...
}

Next we define the initial value that the animation will have, in this case it is 1 so that the component maintains its original size when starting the animation.

componentWillMount() {
  this.scaleValue = new Animated.Value(1);
}

What follows is to define the two functions that we link in the constructor. The first one will be in charge of animating and making the card smaller while the second one will be in charge of returning it to its original size.

handlePressIn() {
  Animated.spring(this.scaleValue, {
    toValue: 0.97 // You can change this value if you want
  }).start();
}

handlePressOut() {
  Animated.spring(this.scaleValue, {
    toValue: 1
  }).start();
}

Now we have to implement the render function as follows:

render() {
  const transformStyle = {
    transform: [{ scale: this.scaleValue }],
    width: cardWidth,
    height: cardHeight,
    marginLeft: 10,
    marginRight: 10,
    marginBottom: 20
  };

  return (
    <TouchableWithoutFeedback
      style={styles.touchableContainer}
      onPressIn={this.handlePressIn}
      onPressOut={this.handlePressOut}
    >
      <Animated.View
        style={transformStyle}
      >
        <StyledCard />
      </Animated.View>
    </TouchableWithoutFeedback>
  );
}

Some notes about the las piece of code:

  • The margin properties in the transformStyle object are completely optional, I use them to give some space between cards.
  • We use TouchableWithoutFeedback because the animation we are trying to achieve will be responsible for giving feedback to the user.

Finally we define the necessary styles and export the component:

const StyledCard = styled.View`
  width: 100%;
  height: 100%;
  border-radius: 10px;
  border-width: 1px;
  border-color: #828282;
  background-color: '#f8f9fb';
`;

const styles = {
  touchableContainer: {
    alignSelf: 'stretch'
  }
};

export default Card;

That's how it looks in the application I made:

I firmly believe that such subtle animations enhance the quality and enhance the user experience of an application. Fortunately, this specific animation is easy to achieve using React Native.

By the way, in this link you can see the complete code.