Viewing an image like in the Facebook Paper App

Inspiration is always gained from lovely inspiring software. The new app from Facebook called Facebook paper has some nifty animation and features. While we shall not recreate all of them, we are looking at just one of them, where you can tilt your phone to view the image

This is similar to the Parallax effect in iOS7, but with a slight difference, in this we scroll the picture based on the tilt rather than just move it a bit. In this tutorial we shall also learn about using accelerometers with GiderosSDK

First the Video


Note: You might see the camera focus the brightness and contrast since the demo was running on my iPhone4 I could not cast my screen to the iMac and record, so I had to record using the iPhone5 in one hand while tilting the iPhone4 with the other (will need to create a rig, if I need to record once more).

Here's the original Facebook paper app video introducing the feature.

Theory

We take a large bitmap and display it on the screen (stage as we refer to it in GiderosSDK) Then we use the accelerometer data and scroll the image left or right, it is actually that simple

How to scroll an image

When we display an image on the screen, by default it is placed at 0,0 co-ordinates. The image extends out of the screen when it is larger than the width of the screen. If we set the x co-ordiante of the image to the current x position - 1, we have scrolled or moved the image 1 pixel to the left. If we move the image to the current x position + 1 then we have scrolled the image to the right. We can do this repeatedly to scroll larger portions of the image.

Caveat

There is one very big issue, if we do not check for the co-ordinates of the image, we can scroll the image entirely off the screen. From the start position, when the image is set at 0,0; if we scroll to the right, we simply add the number of pixels we want to scroll the image by. Actually when the image is at the beginning position, we cannot scroll is to the right it is at the left most part of the image. So when we scroll, we check for the current x position it cannot be more than 0, otherwise it has moved to the right and you will see the stage behind. The same goes for scrolling to the left. When scrolling to the left, the image is moved to the current x position - 1, so the x position will be less than 0, if we continue moving this, we shall soon have scrolled the image past the extreme right and we can again see the stage. So to avoid that, we check for the maximum scroll of screen_Width - image_Width, note the order, since the screen_Width is smaller than the image_Width the resulting value would be a negative number and that is the maximum we want to move the image.

So let's get started

I took an image from my trip to the Taj Mahal, this was a lovely image that I could resize and crop in the form of a long rectangular image. The dimensions of the image are 1632 x 1224 and the app is run in iPhone4 retina mode, so the screen size is 640 x 960 (the 264 pixels at the bottom are not seen) if you want, you can scale the image in the app to fit 960 pixels. first we load up the image and display it on the screen with
local bmp = Bitmap.new(Texture.new("myTajImage.jpg")) -- ou can use your own image and image names accordingly
 stage:addChild(bmp)
next we try to get and set some variables that we shall use through out
local _W = application:getWidth()  -- This provides us with the width of the screen 
 local _bmpW = bmp:getWidth()       -- This provides us with the width of the image
 local speed = 2                    -- This is the speed of the scrolling
To test it we can scroll the image by 200 pixels to the left as
function moveIt(e)
  local _x = bmp:getX() - 1
  bmp:setX(x)
 end 

 stage:addEventListener(Event.ENTER_FRAME, moveIt, self)
when you run the app, you will see the image scroll to the left and then it continues so and scrolls off the screen. So let us now add the checking to stop the scrolling if we get past the bounds we spoke about earlier. change the function moveIt as follows
function scrollLeft()
  local _x = bmp:getX()
  if ((_W - _bmpW) > (_x - speed)) then
   bmp:setX(_W - bmpW)
  else
   bmp:setX(_x - speed)
  end 
 end 

 function moveIt(e)
  scrollLeft()
 end
and now you will note that the scrolling moves till the right most edge of the image and then stops we can similarly have a scrollRight function defined as
function scrollRight()
  local _x = bmp:getX()
  if (_x + speed) > 0 then 
   bmp:setX(0)
  else
   bmp:setX(_x + speed)
  end 
 end 

But that's not tilting

Yes, now we need to listen to the accelerometer to know if the device was tilted or not. Please note that the Gideros simulator or the xCode simulator for iOS devices and also the android simulators do not have an accelerometer built-in so if you want to test, you have to run the code on a device only. (there are programs that connect to a device and pass that to the simulator, but with Gideros when you can run the app on the device with minimal fuss, why bother with work arounds?) Before you can start using the accelerometer, there are a couple of things you need to do 1. Create a new Accelerometer object 2. Start the accelerometer 3. Poll the accelerometer 4. If done, stop the accelerometer This is actually as simple as it looks and can be achieved with simply single lines of code 1. Create a new object
local accel = Accelerometer.new()
2. Start the accelerometer
accel:start()
3. Poll the accelerometer
stage:addEventListener(Event.ENTER_FRAME, moveIt, self)
4. Stop the accelerometer
accel:stop()
Now all we need to do is get the accelerometer data, which can be gotten using the function accel:getAcceleration(). The code local x, y, z = accel:getAcceleration() gets the accelerometer data. One thing to note, that's that, it is for that moment, if you want to get a continuous stream, you have to keep polling it continuously. So we use the Enter_Frame event as follows
function moveIt(e)
  local x, y, z = accel:getAcceleration()
 end 

Checking the tilt

We are interested in tilting the phone left/right only, which means we are interested in the x axis of the accelerometer. If you print the values, you shall note that like our scroll, when the device is tilted to the left, the values are negative and when tilted to the right, the values are positive. We can use this information in our function moveIt as follows
function moveIt(e)
  local x = accel:getAcceleration()
  if x < 0 then 
   scrollLeft()
  else x > 0 then
   scrollRight()
  end 
 end 
The only catch is that the accelerometer is very sensitive and this can cause the image to move slightly even when the phone is flat on a surface, I use a filter, i.e. only if the values are greater than 0.3 or lesser than -0.3 only in that scenario scroll the image left or right.

Homework / Learning

While this is complete, you can add a Y-axis tilt and also inertial scroll, scrolling that increases progressively as seen in the video. One way is to use the value of the tilt and multiply that with the speed and another is to add inertia (no BOX2D, pure maths only)

Happy Tilting your image like the Facebook Paper app. If you do use this in your project, a thank you to me in the credits would be nice and definitely would not hurt you.

Comments

Popular Posts