Move to peek - Like Facebook paper app
The EffectThe 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 TheoryThe 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 NoteFor 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 layersFor 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)
NoteWe 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 upWe 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 layerNow 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_BEGINA 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 endyou 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_MOVEThis 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 endNow 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) endjust 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 fadeThis 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_ENDThis 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 endWell, that's all there is to creating that effect. It really is that simple