Tuesday, October 6, 2015

Timbre demo from Famo.us University using Meteor.js and CoffeeScript

Introduction

As already demonstrated in my previous article Famo.us polaroid tutorial in CoffeeScript and within MeteorMeteor, CoffeeScript and Famo.us are incredibly useful tools for front end or full stack developers. This article just show the same principles with the Famo.us University Timbre tutorial. A live demo is deployed on Meteor's testing ground: http://famoustimbre.meteor.com/.

Show me the code, please

If you find yourself stuck at translating some JS concept to CoffeeScript while playing the nice tutorial from Famo.us University, I provide my code hereafter:
client/stylesheets/app.styl
@import nib

html
font-family: Helvetica

*
-webkit-user-drag: none

body
-webkit-touch-callout: none
user-select: none

client/index.jade
head
title Famo.us Timbre
meta(name='viewport', content='width=device-width, maximum-scale=1, user-scalable=no')
meta(name='mobile-web-app-capable', content='yes')
meta(name='apple-mobile-web-app-capable', content='yes')
meta(name='apple-mobile-web-app-status-bar-style', content='black')
body
+index

template(name='index')

client/lib/famous.coffee
window.Famous ?= {}

client/lib/timbre.coffee
window.Timbre ?= {}

client/startup/famous.coffee
# Import Famous
require 'famous/core/famous'
# Adds the famo.us dependencies
require 'famous-polyfills'
# Wait for document ready
$(document).ready ->
# Load Famo.us libraries
Famous.Engine = require 'famous/core/Engine'
Famous.Surface = require 'famous/core/Surface'
Famous.Transform = require 'famous/core/Transform'
Famous.View = require 'famous/core/View'
Famous.Modifier = require 'famous/core/Modifier'
Famous.StateModifier = require 'famous/modifiers/StateModifier'
Famous.HeaderFooter = require 'famous/views/HeaderFooterLayout'
Famous.ImageSurface = require 'famous/surfaces/ImageSurface'
Famous.FastClick = require 'famous/inputs/FastClick'
Famous.GenericSync = require 'famous/inputs/GenericSync'
Famous.MouseSync = require 'famous/inputs/MouseSync'
Famous.TouchSync = require 'famous/inputs/TouchSync'
Famous.GenericSync.register
'mouse': Famous.MouseSync
'touch': Famous.TouchSync
Famous.Easing = require 'famous/transitions/Easing'
Famous.Transitionable = require 'famous/transitions/Transitionable'
Famous.Timer = require 'famous/utilities/Timer'
# Create main context
Timbre.mainCtx = Famous.Engine.createContext()

client/models/StripData.coffee
Timbre.StripData = [
{title: 'search', iconUrl: 'img/strip-icons/famous.png'}
{title: 'starred', iconUrl: 'img/strip-icons/starred.png'}
{title: 'friends', iconUrl: 'img/strip-icons/friends.png'}
{title: 'settings', iconUrl: 'img/strip-icons/settings.png'}
]

client/index.coffee
ASPECT_RATIO = 320 / 548

Template.index.rendered = ->
$document = $ document
$document.ready ->
docwidth = $document.width()
docheight = $document.height()
if docwidth / ASPECT_RATIO > docheight
screenwidth = docheight * ASPECT_RATIO
screenheight = docheight
else
screenwidth = docwidth
screenheight = docwidth / ASPECT_RATIO
appView = new Timbre.AppView()
mainMod = new Famous.Modifier
size: [screenwidth, screenheight]
Timbre.mainCtx
.add mainMod
.add appView

client/models/AppView.coffee
$(document).ready ->
class Timbre.AppView extends Famous.View
DEFAULT_OPTIONS:
openPosition: 276
transition:
duration: 300
curve: Famous.Easing.inOutBack
posThreshold: 138
velTreshold: 0.75
constructor: (@options) ->
@constructor.DEFAULT_OPTIONS = @DEFAULT_OPTIONS
super @options
@menuToggle = false
@pageViewPos = new Famous.Transitionable 0
@createPageView()
@createMenuView()
@setListeners()
@handleSwipe()
createPageView: ->
@pageView = new Timbre.PageView()
@pageModifier = new Famous.Modifier
transform: =>
Famous.Transform.translate @pageViewPos.get(), 0, 0
@add(@pageModifier).add @pageView
createMenuView: ->
@menuView = new Timbre.MenuView stripData: Timbre.StripData
menuModifier = new Famous.StateModifier
transform: Famous.Transform.behind
@add(menuModifier).add @menuView
setListeners: ->
@pageView.on 'menuToggle', @toggleMenu
toggleMenu: =>
if @menuToggle
@slideLeft()
else
@slideRight()
@menuView.animateStrips()
@menuToggle = !@menuToggle
slideLeft: ->
@pageViewPos.set 0, @options.transition, =>
@menuToggle = false
slideRight: ->
@pageViewPos.set @options.openPosition, @options.transition, =>
@menuToggle = true
handleSwipe: ->
sync = new Famous.GenericSync(
['mouse', 'touch']
, {direction: Famous.GenericSync.DIRECTION_X}
)
@pageView.pipe sync
sync.on 'update', (data) =>
currentPosition = @pageViewPos.get()
@pageViewPos.set Math.max 0, currentPosition + data.delta
if currentPosition is 0 and data.velocity > 0
@menuView.animateStrips()
sync.on 'end', (data) =>
velocity = data.velocity
position = @pageViewPos.get()
if position > @options.posThreshold
if velocity < -@options.velTreshold
@slideLeft()
else
@slideRight()
else
if velocity > @options.velTreshold
@slideRight()
else
@slideLeft()

client/models/PageView.coffee
$(document).ready ->
class Timbre.PageView extends Famous.View
DEFAULT_OPTIONS:
headerSize: 44
constructor: (@options) ->
@constructor.DEFAULT_OPTIONS = @DEFAULT_OPTIONS
super @options
@createLayout()
@createHeader()
@createBody()
@setListeners()
createLayout: ->
@layout = new Famous.HeaderFooter
headerSize: @options.headerSize
layoutModifier = new Famous.StateModifier
transform: Famous.Transform.translate 0, 0, .1
@add(layoutModifier).add @layout
createHeader: ->
backgroundSurface = new Famous.Surface
properties: backgroundColor: 'black'
backgroundModifier = new Famous.StateModifier
transform: Famous.Transform.behind
@layout.header
.add backgroundModifier
.add backgroundSurface
@hamburgerSurface = new Famous.ImageSurface
size: [44, 44]
content: 'img/hamburger.png'
searchSurface = new Famous.ImageSurface
size: [232, 44]
content: 'img/search.png'
iconSurface = new Famous.ImageSurface
size: [44, 44]
content: 'img/icon.png'
hamburgerModifier = new Famous.StateModifier
origin: [0, .5]
align: [0, .5]
searchModifier = new Famous.StateModifier
origin: [.5, .5]
align: [.5, .5]
iconModifier = new Famous.StateModifier
origin: [1, .5]
align: [1, .5]
@layout.header
.add hamburgerModifier
.add @hamburgerSurface
@layout.header
.add searchModifier
.add searchSurface
@layout.header
.add iconModifier
.add iconSurface
createBody: ->
@bodySurface = new Famous.ImageSurface
size: [undefined, true]
content: 'img/body.png'
@layout.content.add @bodySurface
setListeners: ->
@hamburgerSurface.on 'click', =>
@_eventOutput.emit 'menuToggle'
@bodySurface.pipe @_eventOutput
createBacking: ->
backing = new Famous.Surface
properties:
backgroundColor: 'black'
boxShadow: '0 0 20px rgba(0,0,0,0.5)'
@add backing

client/models/MenuView.coffee
$(document).ready ->
class Timbre.PageView extends Famous.View
DEFAULT_OPTIONS:
headerSize: 44
constructor: (@options) ->
@constructor.DEFAULT_OPTIONS = @DEFAULT_OPTIONS
super @options
@createLayout()
@createHeader()
@createBody()
@setListeners()
createLayout: ->
@layout = new Famous.HeaderFooter
headerSize: @options.headerSize
layoutModifier = new Famous.StateModifier
transform: Famous.Transform.translate 0, 0, .1
@add(layoutModifier).add @layout
createHeader: ->
backgroundSurface = new Famous.Surface
properties: backgroundColor: 'black'
backgroundModifier = new Famous.StateModifier
transform: Famous.Transform.behind
@layout.header
.add backgroundModifier
.add backgroundSurface
@hamburgerSurface = new Famous.ImageSurface
size: [44, 44]
content: 'img/hamburger.png'
searchSurface = new Famous.ImageSurface
size: [232, 44]
content: 'img/search.png'
iconSurface = new Famous.ImageSurface
size: [44, 44]
content: 'img/icon.png'
hamburgerModifier = new Famous.StateModifier
origin: [0, .5]
align: [0, .5]
searchModifier = new Famous.StateModifier
origin: [.5, .5]
align: [.5, .5]
iconModifier = new Famous.StateModifier
origin: [1, .5]
align: [1, .5]
@layout.header
.add hamburgerModifier
.add @hamburgerSurface
@layout.header
.add searchModifier
.add searchSurface
@layout.header
.add iconModifier
.add iconSurface
createBody: ->
@bodySurface = new Famous.ImageSurface
size: [undefined, true]
content: 'img/body.png'
@layout.content.add @bodySurface
setListeners: ->
@hamburgerSurface.on 'click', =>
@_eventOutput.emit 'menuToggle'
@bodySurface.pipe @_eventOutput
createBacking: ->
backing = new Famous.Surface
properties:
backgroundColor: 'black'
boxShadow: '0 0 20px rgba(0,0,0,0.5)'
@add backing

client/models/StripView.coffee
$(document).ready ->
class Timbre.StripView extends Famous.View
DEFAULT_OPTIONS:
width: 320
height: 55
angle: -0.2
iconSize: 32
iconUrl: 'img/strip-icons/famous.png'
title: 'Famo.us'
fontSize: 26
constructor: (@options) ->
@constructor.DEFAULT_OPTIONS = @DEFAULT_OPTIONS
super @options
@createBackground()
@createIcon()
@createTitle()
createBackground: ->
backgroundSurface = new Famous.Surface
size: [@options.width, @options.height]
properties:
backgroundColor: 'black'
boxShadow: '0 0 1px rgba(0, 0, 0, 1)'
rotateModifier = new Famous.StateModifier
transform: Famous.Transform.rotateZ @options.angle
skewModifier = new Famous.StateModifier
transform: Famous.Transform.skew 0, 0, @options.angle
@add(rotateModifier)
.add skewModifier
.add backgroundSurface
createIcon: ->
iconSurface = new Famous.ImageSurface
size: [@options.iconSize, @options.iconSize]
content: @options.iconUrl
properties:
pointerEvents: 'none'
iconModifier = new Famous.StateModifier
transform: Famous.Transform.translate 24, 2, 0
@add(iconModifier).add iconSurface
createTitle: ->
titleSurface = new Famous.Surface
size: [true, true]
content: @options.title
properties:
color: 'white'
fontSize: "#{@options.fontSize}px"
textTransform: 'uppercase'
pointerEvents: 'none'
titleModifier = new Famous.StateModifier
transform: Famous.Transform.thenMove(
Famous.Transform.rotateZ @options.angle
, [75, -5, 0]
)
@add(titleModifier).add titleSurface

No comments:

Post a Comment