Building A Slot Machine App in React Native (part #3)

Tamas Szikszai
Oct 13 · 6 min read

In August I challenged myself to create a Slot Machine App in React Native that is not just a proof of concept, but ready for the app store. Relatively quickly I managed to create the basics: a “Spin” button and a set of reels that animate smoothly when you press the button. I was also able to add the logic for evaluating the reels after the spin. If you missed you can watch the first two parts here and here.

When it comes to apps (or any development really) I usually make a POC, to see if the idea is even possible, then I reach out to my designer to create something that I can work with. In this case he had a pretty busy September so the graphics were coming along VERY slowly. Artists… eh?

But last week he sent me the final product and it looks absolutely gorgeous, so it well worth the wait:

Now it’s all on me to code it!

TL;DR: If you want you can watch the session in a video format:

Responsiveness

When it comes to a design that is heavy on images, it’s very difficult to make it work with all screen sizes. Some people prefer using percentage values in their styling but I would advise against it, as some properties can’t take a percentage value.

Instead I’m using the following method:

  1. Identify which way your design have a leeway: It is a bit difficult concept so let me try to explain it using this design as an example. In our case our top and bottom bars are pretty crammed, so all the buttons and text needs to be pixel perfect. The middle section however has a bit of a “give”. If the middle section’s height for example would be slightly smaller, we would just adjust the width of the reelset to compensate for it, and align it to the middle of the section. In this case I would say that our design has a “vertical give”
  2. That means that I will base the sizing of our elements to the ratio between the design’s width and the screen’s width. So lets create a Constant for it in our Constants.js:
export default Constants = {
MAX_WIDTH: Dimensions.get("screen").width,
XR: Dimensions.get("screen").width / 667,
...
}

So now we have a new unit to use when we are sizing our elements. For example if we have a button in Sketch that has the width of 300 we can just write:

width: 300 * Constants.XR

and it will take up exactly the same portion of the screen (on the horizontal axis) as in our design. On the vertical axis however it will take up slightly more / less space, depending on our screen size, but as we discussed it above, it doesn’t matter that much as the main section is easy to adjust.

With that in mind let’s get to the coding

Our design has three sections, I will call them topBar, main and bottomBar:

topBar: {
height: Constants.XR * 53, // this has 3 points bottom padding built in
width: Constants.MAX_WIDTH
},
main: {
height: Constants.MAX_HEIGHT - Constants.XR * 53 - Constants.XR * 71, // 3 top 3 bottom padding
width: Constants.MAX_WIDTH,
alignItems: 'center',
justifyContent: 'center'
},
bottomBar: {
height: Constants.XR * 71,
width: Constants.MAX_WIDTH // this has 3 points top padding built in
},

This is pretty basic stuff. main’s height will be MAX_HEIGHT minus the height of the two bars (53 and 71)

I create 3 Views with the above classes and put them into my container, which has a flexDirection: ‘column’. Then I go ahead and export the background images from Sketch:

As you can see the bottom layer has 3 points worth of shadow, so I adjusted the container’s height for it.

I export 3 different sizes for each of the images. This will help the performance of the final product as android / ios won’t have to scale them based on the pixel density. I add these images to assets/img and add a reference to them in assets/Images.js. Then I just need to add them as an absolutely positioned Image inside my container Views.

I won’t go into detail on how to add each element, otherwise this post would be super long, but you can just watch the video above where I explain everything in detail.

Buttons

There are a ton of buttons in this design and my designer provided me with images for different states:

Since React Native doesn’t have a built in image based button, I have to create my own. I will create a file in components/TouchableButton.js

import React, {Component} from 'react';
import { Text, View, TouchableWithoutFeedback, Image } from 'react-native';
import Images from '../assets/Images';
export default class TouchableButton extends Component {
constructor(props) {
super(props);
this.images = {
"active": Images[this.props.image],
"pushed": Images[this.props.image + "Pressed"],
"inactive": Images[this.props.image + "Inactive"]
}
this.state = {
status: "active"
}
}
handlePressIn = () => {
if (this.props.inactive){
return
}
this.setState({
status: "pushed"
})
this.props.onPress && this.props.onPress()
}
handlePressOut = () => {
if (this.props.inactive){
return
}
this.setState({
status: "active"
})
}
renderContent = () => {
if (this.props.text){
return (
<Text style={this.props.textStyle}>{this.props.text}</Text>
)
}
}
render() {
const content = this.renderContent()
const status = this.props.inactive ? "inactive" : this.state.status;
return (
<TouchableWithoutFeedback onPressIn={this.handlePressIn} onPressOut={this.handlePressOut}>
<View style={[this.props.style, { justifyContent: 'center', alignItems: 'center'}]}>
<Image source={this.images[status]} style={{ width: this.props.style.width, height: this.props.style.height, position: 'absolute' }} resizeMode={this.props.resizeMode || "stretch"} />
{content}
</View>
</TouchableWithoutFeedback>
)
}
}

This component will receive an image reference as a string and will change the image by adding a “Pressed” string to the end of the reference when the button is pressed. Similarly it will add an “Inactive” string to the end if the component receives the inactive prop.

It also accepts a text prop. If it’s passed in, then it will render a Text element with styling defined in the textStyle prop.

After importing TouchableButton into our main js file we can add the “Spin” button in our bottomBar container:

<TouchableButton
text="SPIN"
textStyle={[styles.buttonText, { marginTop: -5 }]}
onPress={() => {}}
style={styles.buttonSpin}
inactive={false}
image="buttonSpin" />

With the styling of

buttonSpin: {
position: 'absolute',
right: Constants.XR * 13,
top: Constants.XR * 17,
width: Constants.XR * 138,
height: Constants.XR * 48,
},
buttonText: {
includeFontPadding: false,
justifyContent: 'center',
color: 'white',
textShadowColor: '#00006C01',
fontFamily: 'Ubuntu-Bold',
textShadowOffset:{width: 1, height: 1},
textShadowRadius:10,
fontSize: Constants.XR * 24,
alignContent: 'center',
},

All the positioning numbers are coming from Sketch, and I can trust it will be laid out perfectly because of the XR constant.

I’m using a custom font (Ubuntu-Bold) which I imported using the technique in this article.

The rest of the design is pretty much rinse and repeat, except for the Sound button in the bottom left corner. This is not so much a button but a switch so I decided to create a component for it, even though it’s very similar to TouchableButton. The component will be in components/TouchableSwitch.js with the following code:

import React, {Component} from 'react';
import {Text, View, TouchableWithoutFeedback, StyleSheet, Image, Alert} from 'react-native';
import Images from '../assets/Images';
export default class TouchableSwitch extends Component {
constructor(props) {
super(props);
this.images = {
"active": Images[this.props.image],
"inactive": Images[this.props.image + "Inactive"],
}
this.state = {
status: this.props.status
}
this.onSwitch = this.props.onSwitch || null
}
handlePressOut = () => {
let newStatus = this.state.status === "active" ? "inactive" : "active";
this.setState({
status: newStatus
})
if (this.onSwitch){
this.onSwitch(newStatus);
}
}
setStatus = (status) => {
this.setState({
status: status
})
}
render() {
return (
<TouchableWithoutFeedback onPressOut={this.handlePressOut}>
<View style={this.props.style}>
<Image source={this.images[this.state.status]} style={{ width: this.props.style.width, height: this.props.style.height, position: 'absolute' }} resizeMode={this.props.resizeMode || "stretch"} />
</View>
</TouchableWithoutFeedback>
)
}
}

And that’s about it. If you are curious about the nitty gritty details of coding this design, check out my video here.

For the final product I decided to move around a couple of buttons but besides that it’s pretty much pixel perfect and absolutely gorgeous (the gif doesn’t give it justice, but trust me it’s 60 fps and looks amazing)

If you liked this post please follow me, so you will get notified about the upcoming episodes. Also check out my YouTube channel where I post similar content fairly regularly.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade