Move to peek - Like Facebook paper app

It is interesting to note the amount of amazing effects added into Facebook paper app. In the next few tutorials, while we shall recreate these effects which are useful for medium to advance level developers, Beginners can read and understand the underlying principles which make learning a bit more interesting (visually atelast)

The Effect

The effect that we are trying to replicate is moving the current view and displaying the view underneath which fades in slowly based on how much have we pulled the upper layer to peek underneath. The same in video form.

The Theory

The upper layer is moveable and can be slid vertically. As it is moved up, it uncovers the previous view underneath. Instead of simply displaying it uncovered, we fade the same from black. It is visible the more we pull the upper layer off. If we let go, the upper layer animated back to cover it and if we have pulled the upper layer to the threshold, it is peeled off displaying the layer underneath.

Technical Note

For the purpose of this demo, we are using images to represent the two layers, if it were in an app, you would have had UIViews (iOS). Even if you were trying this with Lua, you would have to create two layers / groups.

Creating the layers

For recreating this effect, we shall have three layers, the front, the bottom and the shroud. The placement is the bottom layer, then the shroud and then finally the front. The shroud is a big black 'Shroud' that obscures the bottom view. We change the opacity of this layer to show / hide the bottom layer based on how much the front layer has been moved.
local bottom = Bitmap.new(Texture.new("image1.jpg"))
 stage:addChild(bottom)
 local shroud = rectangle(0, 0, _W, _H, 0x000000)
 stage:addChild(shroud)
 local front = Bitmap.new(Texture.new("image2.jpg"))
 stage:addChild(front)

Note

We have created a function called rectangle that takes five parameters, the x, y, width, height and the color. This creates a shape object with a black rectangle. This function is defined as
function rectangle(x, y, width, height, color)
  local sh = Shape.new()
   sh:clear()
   sh:setLineStyle(0,0x00,0)
   sh:setFillStyle(Shae.SOLID, 0x00, 1)
   
   sh:beginPath()
   sh:moveTo(0,0)
   sh:lineTo(_W, 0)
   sh:lineTo(_W, _H)
   sh:lineTo(0, _H)
   sh:lineTo(0,0)
   sh:closePath()
   sh:endPath()

   sh:setPosition(x, y)

  return sh
 end 

Setting up

We would also need a couple of other variables that will help us work with the demo code, this will be at the top of your main.lua before we create the layers. The initialization code is
local animate = false
 local speed = 0.2
 local _W = application:getDeviceWidth()
 local _H = application:getDeviceHeight()

Moving the top layer

Now we can see the top layer covering up both the shroud and the bottom layer, we want this to be moveable. So to do so, we need to attach a handler that will handle the touch events. We need to have three handlers, for each of the three stages of touch, the TOUCH_BEGIN when the finger touches the screen, the TOUCH_MOVE when the finger still touching the screen is moved and the TOUCH_END when the finger is released off the screen. We add the listeners to the front layer as follows
front:addEventListener(Event.TOUCHES_BEGIN, front.onDown, front)
 front:addEventListener(Event.TOUCHES_MOVE, front.onMove, front)
 front:addEventListener(Event.TOUCHES_END, front.onUp, front)
and we need to add these functions or we would get an error that these functions do not exist.

TOUCHES_BEGIN

A small note about handling touches, you might feel that much of the code is redundant, however it helps to create good habits, not saying that this is the best way.
function front:onDown(e)
  self.focus = true
  self.sy = e.touch.y or 0
 end 
you might have noticed that the function is not simply called onDown, but instead front:onDown(e), which is the other way of saying front.onDown(self,e) and therefore in the addEventListener we are using front.onDown not just onDown. We first create a new property called focus and set it to true, this is useful as we can check if the touch did originate on this object or not, because in the onMove, when we start to move the layer, what if we had never started our touch on this object, we would be moving this layer like a ghost layer. However in our case the layer is full screen so we could not have not initiated our touch anywhere other than on the layer. Next we set another property called sy (or as refer to it in my mind as the start y) this is the point where the touch started. It is handy to know this to be able to move the image in reference to this point. You will see what I mean, set this value as 0 instead of e.touch.y and see how it goes.

TOUCHES_MOVE

This is where the bulk of our code lies. First we check if we have the focus (touch started on this object) otherwise return. Next we calculate the amount the touch has moved from the last time and then add that delta (differential) to the current position of the front image.
function front:onMove(e) 
  if self.focus ~= true then return end
  
  local dy = e.touch.y - self.sy
  local y = self:getY()

  self:setY( y + dy )
  self.sy = e.touch.y
 end 
Now you will see that you can move the image both ways, peek up or peek down, we want this moving only in one direction not both, so we ignore if the layer is moved downwards to peek.
if self:getY() > 0 then self:setY(0) end
just that one line added to the function above only allows us to move the layer upwards to uncover and pull it back to cover, but not pull it down.

Adding the fade

This is rather simple, we simply need to set the alpha property for the shroud depending on the distance the front layer has been moved, we use a bit of math for that as
local alpha = (math.abs(self:getY()) / _H * 0.75)
  shroud:setAlpha ( 1 - al )

TOUCHES_END

This is the last event handler that we work with and this resets the movement or changes the layer as required.
function front:onUp(e)
  self.focus = nil
  local y = math.abs(self:getY())

  if y > _H/1.5 then
    self:setY(-_H)
    shroud:setAlpha(0)
  else
    self:setY(0)
   shroud:setAlpha(1)
  end

 end 
Well, that's all there is to creating that effect. It really is that simple

Learning

There are a couple of things that one has learned in this tutorial and they are 1. Creating a Rectangle 2. Moving a layer/image with touch - Handling Touches 3. Creating the Peek effect

Advanced / Homework

To hone your skills, you can have additional features, you could use tweening to reset the top layer back instead of using the self:setY(0), tweening it will provide a smooth movement of it sliding back. You can also try to create an image viewer where everytime you uncover the layer, the bottom layer loads a new image and the top layer is the image that is showing (i.e. the old bottom layer)

For Sale

If you would want the complete source in a zip form of the image viewer that allows you to view the images by flipping them over in this peek effect, you can pay via PayPal and I can send you the zip file.

Comments

Popular Posts