Meteor-React-Ionic Mobile App Part 6.2: Tinder Selector

Sam Corcos
5 min readAug 20, 2015

We’re going to implement a “swipe right for yes, swipe left for no” interface that has become almost ubiquitous among apps — especially dating apps. We covered the basics of Tinder transitions in a previous article, and we will build on that to implement this interface.

Your best bet is to start with the repo from Part 6.1: Tinder Transitions, but you can also start from the beginning and build your own.

When this is finished, your app should look like the one to the left.

NOTE: Be aware that if you clone the repo, there are a few quirks with the meteoric:ionic-sass package that will crash the app the first time you run it. All you have to do to solve this is kill the process and re-run Meteor.

We’re also going to add a small rotation, just like you see in Tinder.

Step 1

The first step is a messy one. We need to pull our card out of our Home component and make a new Card component. Basically, everything that is in the Home component needs to go into the new Card component, and we will use the Home component to manage the cards.

Once we do this, we can use the Card component to render multiple cards and allow the cards to destroy themselves after they’ve been accepted or declined by the user.

Since pretty much everything is being moved from one component to another, it’s probably just easier to change the name of Home to Card, and then creating a new Home component:

Card = React.createClass({

…and for our new Home component:

Home = React.createClass({
render() {
return <h1>New Home</h1>
}
})

Step 2

Now that we’ve gotten that out of the way, we need to build out our Home component. We’re going to hardcode some data and store it in state so we can easily remove cards from the DOM. We can do this by simply adding:

getInitialState() {
return {
cards: [{id:1},{id:2},{id:3},{id:4},{id:5},{id:6}]
}
},

Then we want to write a function for removing a card from state. We’re going to do this by setting state to a new set of cards that filters out the card we want to remove.

removeCard(id) {
this.setState({
cards: this.state.cards.filter((card) => card.id != id )
})
},

Then we need a function to create the list of cards. In the function renderCards, we’re going to map a function that creates a Card component for each item in this.state.cards (all of the cards remaining). We’re also going to pass the id as the unique key, the remove function, and the data contained in the card:

renderCards() {
return this.state.cards.map((card) => {
return <Card
key={card.id}
card={card}
remove={ () => this.removeCard(card.id)}
/>
})
},

The last thing to change on our Home component is our render function, which we would like to call the renderCards function we described above:

render() {
return <div>{this.renderCards()}</div>
}

Step 3

The next step takes us back to our Card component. We need to focus on where the card ends up in order to determine whether it was accepted or declined. We’re going to set these break points at 50px from either end of the screen.

Within our moveCardEnd function, we want to set three conditionals to keep track of what to do with our card based on the touch events. Remove everything currently in this function and replace it with the code blocks below.

For our first conditional, we want to see if someone swiped left. We’re going to check if the touch event ended at below 50px on the x-axis. If so, we’re going to translate the card along the x-axis by 1000 pixels to the left and ease it over:

moveCardEnd(e) {
if (e.changedTouches[0].pageX < 50) {
this.setState({
x: -1000,
y: 0,
dragging: "all 0.5s ease"
})
}
...

For our next conditional, we want an else if to check for a swipe right, where we do the same thing as above but mirrored:

...
} else if (e.changedTouches[0].pageX > (window.innerWidth - 50)) {
this.setState({
x: 1000,
y: 0,
dragging: "all 0.5s ease"
})
}
...

And for our last conditional, if the card ends somewhere in between the two break points, we’re going to send the card back to where it started:

...
} else {
this.setState({
x: 0,
y: 0,
dragging: "all 0.5s ease"
})
}

Step 4

Almost there! To finish this, we need to do four more things: 1) change the text of the card, 2) add rotation, 3) destroy the card when it’s been selected, and 4) move the other cards up to take the place of cards that are removed.

First let’s change the text of the card. Remove the placeholder text and add the following:

...
<div className="item item-text-wrap">
{"This is card id: " + this.props.card.id}
</div>
...

Then let’s tackle rotation. Within our cardStyle object, add the following to give our card some rotation. We’re basing the degree of rotation on the this.state.x, which as you may recall is the distance in pixels the card has travelled from its original location:

...
this.state.y + "px) " +
"rotate("+this.state.x/10 + "deg)",
transition: this.state.dragging
...

To destroy the card after it’s been selected, add the following setTimeout after setState in our conditionals in which the card has been selected (here and here):

Meteor.setTimeout(this.props.remove, 500)

And finally, to move the other cards up, we’re going to set a conditional for our cardStyle to move the other cards up by 71px (the height of our cards) if the state of x or y is 1000 or -1000 (meaning it has been selected):

...
transition: this.state.dragging
}
if (this.state.x <= -1000 || this.state.x >= 1000) {
cardStyle.marginBottom = '-71px'
}
...

And now we’re done! Next, we’ll add data and see the results of our affirmative swipes in a different route.

Sam Corcos is the lead developer and co-founder of Sightline Maps, the most intuitive platform for 3D printing topographical maps, as well as LearnPhoenix.io, an advanced tutorial site for building scaleable production apps with Phoenix and React.

Additional

--

--

Sam Corcos

Software developer, founder, author - CarDash - Learn Phoenix - SightlineMaps.com