Think LiveCode can’t be sexy? It can be when you use it to swipe through photos and rate them based on whatever fancy strikes you. This article will describe how to build a Tinder-style photo rating behavior that uses swipe gestures to approve or reject photos (this structure is often called a “card stack” in web development circles). Swiping a photo triggers dragging that provides two levels of user feedback and can be customized.
Before you get started, you’ll need to get yourself a copy FontAwesome, a free glyph font (also known as an icon font) that contains hundreds of useful icons for use on web sites and in apps. Download the font and load it on your system so that LiveCode has access to all the tasty icons.
Also, note that this demo was built using LiveCode 7 on Mac OS X.
Now, if we’re going to build a sexy photo rating app, we need to start with a sexy name. Since this app is going to rely heavily on swiping, let’s do what all the trendy startups do and choose a name without vowels: swypr.
Sweet. Now we can hire some Silicon Valley interns to get on the phones and start hitting up venture capitalists with our wicked cool app idea. But if we’re going to pitch VC’s, we’ll need to build a demo to show the concept. To help you with this important task, a prototype stack has been made available for you, so you can get investors drooling and throwing money at you.
To view the stack in LiveCode, copy the following line and execute it in your LiveCode message box:
Once the stack has loaded, start swiping, er, swypng. You’ll see that swiping right tints the photo green and stamps it with a thumbs up icon (keeper), while swiping left tints the photo red and flags it with your disapproval (as if). Notice that the photo can be dragged anywhere on the card and rotates in the direction of the swipe. Sexy, right? There’s also a “change my mind” threshold that will send the photo back to the central starting point if the swipe distance is less than a minimum value.
This direct interaction combined with image rotation makes for a more visceral and satisfying user experience, compared to simply tapping a button. One might even call it — you guessed it: “sexy.”
Here’s a representation of the app structure which is isn’t particularly complicated, but will illustrate how things are arranged.
The photo group indicated has a behavior assigned to it that handles all the interaction, while code in the card script manages the display of the photos. Using a behavior script isn’t required, but it works nicely here to limit interaction to the group, and makes it possible to add additional mouse/touch processing to the group, if desired.
HOW IT WORKS
When the photo group is touched/clicked, a snapshot image of the group is generated, and snapshot data for each icon state (approve and reject) is stored in 2 local variables. The source object for the icons is a button located off screen, which has its textFont set to FontAwesome. Icons are assigned using the button using the numToCodepoint function since the characters we want are high level Unicode symbols in the font. And we’re generating snapshots of these elements because currently the only objects that can be rotated in LiveCode are images and graphics.
While the swipe event takes place, the behavior script applies the appropriate icon data to the overlay image, based on the direction of the swipe, and rotates both the photo group and icon snapshots. Since the icon is displayed in a separate image, it can change independently of the photo group snapshot.
Along with some basic initialization handlers, the card script has a routine that replaces the current photo data with text data of the next photo to be displayed. All 15 photos in this stack are stored as custom properties of the card. Of course, the shipping version of swypr will access real photos in friends’ Instagram feeds, but for now the photos are stored internally for demo purposes (and to wow investors).
Let’s look at the meat of the demo: the photo group’s behavior script. The behavior button sits in a small resources substack of the swypr mainstack.
The script of the button is shown below — it’s commented in the demo stack so you can see what takes place in each step of the interaction.
First we set up some variables to store various values for use in the handlers below, and establish the “swipe threshold” value: the minimum distance an image needs to be dragged to be considered a valid swipe.
local approveIt, rejectIt
constant theSwipeThreshold = 100
The mouseDown handler generates the snapshots that are used during while dragging: one snapshot of the photogroup, and one of the icon overlay. This script also loads the next image to be displayed into the photo group so we can see the upcoming image while the current image image is being dragged.
— STORE THE INITIAL HORIZONTAL TOUCH POSITION (X)
put mouseH() into cX
— STORE THE VERTICAL TOUCH OFFSET FROM THE CENTER OF THE GROUP
put item 2 of loc of me – mouseV() into theVoffset
— GENERATE SNAPSHOT OF THE PHOTO GROUP
import snapshot from me
put long id of last img into theSnapshot
put loc of me into theLoc
set loc of theSnapshot to theLoc
set resizeQuality of theSnapshot to “good”
set colorOverlay of theSnapshot to defaultColorOverlay()
set layerMode of theSnapshot to “dynamic”
— CREATE APPROVE/REJECT ICON OVERLAYS FOR THE SNAPSHOT
— (USING FONTAWESOME GLYPH FONT)
— THESE ICON IMAGES COULD BE PRE-BUILT AND STORED, BUT BY BUILDING
— THEM DYNAMICALLY, THE ICONS CAN BE CHANGED AT WILL.
set label of btn “status” to numToCodepoint(61575)
export snapshot from btn “status” to approveIt as PNG — GENERATE APPROVE OVERLAY DATA
set label of btn “status” to numToCodepoint(61576)
export snapshot from btn “status” to rejectIt as PNG — GENERATE REJECT OVERLAY DATA
put long id of it into theStatus
set loc of theStatus to loc of theSnapshot
set resizeQuality of theStatus to “good”
set text of theStatus to empty
set ink of theStatus to “blendScreen”
set the uCurrentState of theStatus to empty
set layerMode of theStatus to “dynamic”
dispatch “loadNextPhoto” to this cd
put true into allowSwipe
The mouseMove portion of the script handles the all drag action: it tracks the X,Y position of the user’s mouse/touch, and positions/rotates the snapshots. It also swaps the good/bad icon snapshots based on whether the snapshot is dragged right or left, and applies the appropriate green or red color to the colorOverlay graphic effect of the photo snapshot.
if not allowSwipe then exit mouseMove
put X,0 into p1
put cX,0 into p2
put getDistance(p1,p2) into theDistance
if X > cX then multiply theDistance by -1
if theDistance >= 0 then
put 255,0,0 into theColor — RED
put “unhappy” into theState
put rejectIt into theMode
put 0,255,0 into theColor — GREEN
put “happy” into theState
put approveIt into theMode
— MANAGE SNAPSHOT
rotateImage theSnapshot, theDistance
put colorOverlay of theSnapshot into theArray
put theColor into theArray[“color”]
put min(180,round(abs(theDistance * 0.8))) into theArray[“opacity”]
set colorOverlay of theSnapshot to theArray — APPLY COLOR OVERLAY TO THE SNAPSHOT
put theLoc into theNewLoc
subtract theDistance from item 1 of theNewLoc
put Y + theVoffset into item 2 of theNewLoc
set loc of theSnapshot to theNewLoc — POSITION THE SNAPSHOT
— MANAGE ICON STATUS
rotateImage theStatus, theDistance
if the uCurrentState of theStatus <> theState then
set text of theStatus to theMode
set the uCurrentState of theStatus to theState
set loc of theStatus to theNewLoc — POSITION THE OVERLAY
The rotation function is separated out in the script so it can be easily applied to both snapshots and called wherever needed.
— DRAGGING LEFT = POSITIVE ROTATION ANGLE, DRAG RIGHT = NEGATIVE
set angle of pImage to (pDistance/8) — ADJUST “8” TO INCREASE OR DECREASE ROTATION
When the user is done swiping, the mouseUp and mouseRelease handlers stop all dragging action, clear out the temporary snapshots, and increment the photo count (after a completed swipe). It’s always good practice to employ a mouseRelease handler along with mouseUp in case the user’s mouse/touch is released outside the dragged object.
put false into allowSwipe
if not exists(theSnapshot) then exit clearSwipe
put loc of theSnapshot into theSnapLoc
put 0 into item 2 of theSnapLoc
put loc of me into myLoc
put 0 into item 2 of myLoc
if getDistance(theSnapLoc,myLoc) <= theSwipeThreshold then
put true into revertFlag
dispatch “updateCountStatus” to this cd
repeat with N = 10 to 100 step 10
set blendLevel of theSnapshot to N
set blendLevel of theStatus to N
wait 0 millisecs
if revertFlag then dispatch “revertPhoto” to this cd
if exists(theStatus) then delete theStatus
Remember the swipe threshold variable? The snapBack handler takes care of returning the photo snapshot to its starting point if the swipe distance doesn’t exceed the swipe threshold value set at the beginning of the script. To make life easier, the status (thumb) snapshot is deleted before sending this command, so we only need to reset the photo snapshot.
put angle of theSnapshot into theAngle — USE THE ANGLE OF ROTATION AS THE NUMBER OF STEPS TO RESTORE
put theAngle into theSteps
put colorOverlay[“opacity”] of theSnapshot into theOpacity
put 1 into theDifference
if theAngle > 180 then
put 360 – theAngle into theSteps
put -1 into theDifference
put max(1,theSteps) into theSteps — PREVENT DIVIDE BY ZERO ERROR
put (item 1 of loc of theSnapshot – item 1 of loc of me) / theSteps into hStep
put (item 2 of loc of theSnapshot – item 2 of loc of me) / theSteps into vStep
put theOpacity / theSteps into oStep
put loc of theSnapshot into theLoc
subtract hStep from item 1 of theLoc
subtract vStep from item 2 of theLoc
subtract theDifference from theAngle
subtract oStep from theOpacity
set loc of theSnapshot to theLoc
set angle of theSnapshot to theAngle
set colorOverlay[“opacity”] of theSnapshot to round(theOpacity)
wait 0 millisecs
set loc of theSnapshot to loc of me
Remember how I said the behavior can be customized? The most obvious options to change are the green/red color overlays and the rating icons. For example, thumb images don’t go over well in certain parts of the world, so you could change the FontAwesome glyph ids to something like 61720 (happy face) and 61721 (sad face). You could also change the degree of rotation if, for example, your app ran in landscape orientation and more space was available to swipe.
Another option is to replace item 2 of theNewLoc value in the mouseMove handler with item 2 of the loc of the photo group. This change limits the movement of the snapshot to horizontal motion only, producing a slightly more formal/rigid feel that might be more suited to a product catalog.
ROOM FOR IMPROVEMENT
While the interaction that swypr offers is pretty good, it could be made sexier by applying some inertia to the swipe. This would enable the snapshot to slide off the screen on its own with a bit of momentum behind it, instead of halting abruptly as soon as the swipe event ends. But the engineering team will have to pull a couple of all-nighters to finish up that feature, so we’ll save that effect for our meeting with the investors…
Now that you have the basics for building a Tinder-style rating app, what will you make? You don’t have to limit yourself to dating prospects: you could display real estate photos, text passages, song clips, or references to almost any type of media.
Sure, photos of people might be alluring, but when all is said and done, some of the sexiest things worth sharing are ideas.