A famo.us example app. Showcases our javascript powered infinite scrollview, and multiple pages. All data is sent over firebase.
Created
January 31, 2014 20:32
-
-
Save justbc/8742548 to your computer and use it in GitHub Desktop.
A Pen by Brian C.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!-- | |
Join over 70,000 developers in our beta program at http://famo.us. | |
Famo.us is a free opensource Javascript development platform that dan build apps, games and interfaces using HTML5 and WebGL. | |
What makes us unique is that we have built a pure JS renderer and 3D physics engine and we've wrapped those core engines with an enterprise grade platform that's easy to use. | |
Each week we release new examples. If you are a blogger and would like early access so you can write about them conact [email protected]. | |
We are expecting to be in full public beta late February of 2014. | |
--> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//CodePen Evaluation License | |
// | |
//Copyright (c) 2013 Famo.us, Inc. | |
// | |
// Non-sublicensable permission is hereby granted, free of charge, | |
// to any person obtaining a copy of this software and associated | |
// documentation files directly from codepen.io (the "Software"), solely to | |
// internally make and internally use copies of the Software to test, | |
// explore, and evaluate the Software solely in such person’s non-commercial, | |
// non-production environments, provided that the above copyright | |
// notice and this permission notice shall be included in all copies or | |
// substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
Famous(function(require) { | |
// import dependencies | |
var FamousEngine = require('famous/Engine'); | |
var FastClick = require('famous-sync/FastClick'); | |
var Scrollview = require('famous-views/Scrollview'); | |
var ViewSequence = require('famous/ViewSequence'); | |
var Surface = require('famous/Surface'); | |
// boilerplate: the application template (normally in a separate require) | |
var View = require('famous/View'); | |
var EventHandler = require('famous/EventHandler'); | |
var OptionsManager = require('famous/OptionsManager'); | |
var RenderNode = require('famous/RenderNode'); | |
var Utility = require('famous/Utility'); | |
var HeaderFooterLayout = require('famous-views/HeaderFooterLayout'); | |
var EdgeSwapper = require('famous-views/EdgeSwapper'); | |
var NavigationBar = require('famous-widgets/NavigationBar'); | |
var TitleBar = require('famous-widgets/TitleBar'); | |
function App(options) { | |
// extend from view | |
View.apply(this, arguments); | |
// create the layout | |
this.layout = new HeaderFooterLayout(); | |
// create the header | |
this.header = new TitleBar(this.options.header); | |
// create the navigation bar | |
this.navigation = new NavigationBar(this.options.navigation); | |
// create the content area | |
this.contentArea = new EdgeSwapper(this.options.content); | |
// link endpoints of layout to widgets | |
this.layout.id['header'].link(this.header); | |
this.layout.id['footer'].link(Utility.transformInFront).link(this.navigation); | |
this.layout.id['content'].link(Utility.transformBehind).link(this.contentArea); | |
// assign received events to content area | |
this.eventInput.pipe(this.contentArea); | |
// navigation events are app events | |
EventHandler.setOutputHandler(this, this.navigation); | |
// declare the render nodes | |
this._currentSection = undefined; | |
this._sections = {}; | |
this._sectionTitles = {}; | |
// respond to the the selection of a different section | |
this.navigation.on('select', function(data) { | |
this._currentSection = data.id; | |
this.header.show(this._sectionTitles[data.id]); | |
this.contentArea.show(this._sections[data.id].get()); | |
}.bind(this)); | |
// assign the layout to this view | |
this._link(this.layout); | |
}; | |
App.prototype = Object.create(View.prototype); | |
App.prototype.constructor = App; | |
App.DEFAULT_OPTIONS = { | |
header: { | |
size: [undefined, 50], | |
inTransition: true, | |
outTransition: true, | |
look: { | |
size: [undefined, 50] | |
} | |
}, | |
navigation: { | |
size: [undefined, 50], | |
direction: Utility.Direction.X, | |
buttons: { | |
inTransition: true, | |
outTransition: true | |
} | |
}, | |
content: { | |
inTransition: true, | |
outTransition: true, | |
overlap: true | |
} | |
}; | |
App.prototype.getState = function() { | |
return this._currentSection; | |
}; | |
App.prototype.section = function(id) { | |
// create the section if it doesn't exist | |
if(!(id in this._sections)) { | |
this._sections[id] = new RenderNode(); | |
// make it possible to set the section's properties | |
this._sections[id].setOptions = (function(options) { | |
this._sectionTitles[id] = options.title; | |
this.navigation.defineSection(id, { | |
content: '<span class="icon">' + options.navigation.icon + '</span><br />' + options.navigation.caption | |
}); | |
}).bind(this); | |
} | |
return this._sections[id]; | |
}; | |
App.prototype.select = function(id) { | |
this._currentSection = id; | |
if(!(id in this._sections)) return false; | |
this.navigation.select(id); | |
return true; | |
}; | |
// boilerplate: end | |
var FeedItem = require('famous-widgets/FeedItem'); | |
var Transitionable = require('famous/Transitionable'); | |
var OptionsManager = require('famous/OptionsManager'); | |
var Matrix = require('famous/Matrix'); | |
var userInfoFirebase = null; | |
var userInfo = {}; | |
function SparkItem(options) { | |
FeedItem.apply(this, arguments); | |
if(options) this.setOptions(options); | |
this.inState = new Transitionable(0); | |
this.inState.set(1, this.options.inTransition); | |
}; | |
SparkItem.prototype = Object.create(FeedItem.prototype); | |
SparkItem.prototype.constructor = SparkItem; | |
SparkItem.DEFAULT_OPTIONS = OptionsManager.patch(FeedItem.DEFAULT_OPTIONS, { | |
classes: ['tweet'], | |
size: [undefined, 80], | |
inTransition: {curve: 'easeOut', duration: 500} | |
}); | |
SparkItem.setFirebase = function(firebase) { | |
userInfoFirebase = firebase; | |
}; | |
SparkItem.prototype.render = function(input) { | |
var result = FeedItem.prototype.render.apply(this, arguments); | |
if(!this.inState.isActive()) return result; | |
else return {opacity: this.inState.get(), target: result}; | |
}; | |
SparkItem.prototype.setContent = function(content) { | |
var userId = content['author']; | |
var iconUrl = 'content/default-pic.gif'; | |
if(userId in userInfo) { | |
iconUrl = userInfo[userId]['pic']; | |
} | |
else { | |
var userRef = userInfoFirebase.child('people').child(userId); | |
userRef.on('value', function(dataSnapshot) { | |
userInfo[userId] = dataSnapshot.val(); | |
if(userInfo[userId]['pic']) this.setContent(content); | |
}.bind(this)); | |
} | |
var feedContent = {icon: iconUrl, source: content['by'], time: content['timestamp'], text: content['content']}; | |
return FeedItem.prototype.setContent.call(this, feedContent); | |
}; | |
// define the options | |
var headerOptions = { | |
look: {classes: ['header']}, | |
inTransition: {curve: 'easeOutBounce', duration: 375}, | |
outTransition: {curve: 'easeIn', duration: 225}, | |
overlap: false | |
}; | |
var navigationOptions = { | |
buttons: { | |
onClasses: ['navigation', 'on'], | |
offClasses: ['navigation', 'off'], | |
inTransition: {curve: 'easeInOut', duration: 150}, | |
outTransition: {curve: 'easeInOut', duration: 150} | |
} | |
}; | |
var contentOptions = { | |
inTransition: {curve: 'easeOutBounce', duration: 500}, | |
outTransition: {duration: 300}, | |
overlap: true | |
}; | |
// create the App from the template and hook it into the context | |
var myApp = new App({header: headerOptions, navigation: navigationOptions, content: contentOptions}); | |
var mainDisplay = FamousEngine.createContext(); | |
mainDisplay.link(myApp); | |
FamousEngine.pipe(myApp); | |
// setup Firebase | |
var firebase = new Firebase('https://firefeed.firebaseio.com/'); | |
SparkItem.setFirebase(firebase); | |
// create the 'Home' section | |
var homeSection = myApp.section('home'); | |
homeSection.setOptions({ | |
title: '<span class="bird"></span>', | |
navigation: {caption: 'Home', icon: '<span class="entypo">⌂</span>'} | |
}); | |
var homeItems = new ViewSequence(); | |
var homeScroll = new Scrollview(); | |
homeSection.link(homeScroll); | |
// create the 'Connect' section | |
var connectSection = myApp.section('connect'); | |
connectSection.setOptions({ | |
title: 'Connect', | |
navigation: {caption: 'Connect', icon: '@'} | |
}); | |
var connectItems = new ViewSequence(); | |
var connectScroll = new Scrollview(); | |
connectSection.link(connectScroll); | |
// create the 'Discover' section | |
var discoverSection = myApp.section('discover'); | |
discoverSection.setOptions({ | |
title: 'Discover', | |
navigation: {caption: 'Discover', icon: '#'} | |
}); | |
var discoverItems = new ViewSequence(); | |
var discoverScroll = new Scrollview(); | |
discoverSection.link(discoverScroll); | |
// create the 'Me' section | |
var meSection = myApp.section('me'); | |
meSection.setOptions({ | |
title: 'Me', | |
navigation: {caption: 'Me', icon: '<span class="entypo">👤</span>'} | |
}); | |
// stubbed default to Mark for demo purposes | |
var myAuth = { | |
'id': '819290432', | |
'pic': 'https://graph.facebook.com/819290432/picture/?width=200&height=200&return_ssl_resources=1', | |
'fullName': 'Mark Lu', | |
'location': 'San Francisco' | |
}; | |
var profile = new Surface({ | |
size: [undefined, 160], | |
classes: ['profile'], | |
content: '<img src="' + myAuth['pic'] + '" /><p>' + myAuth['fullName'] + ' • ' + myAuth['location'] + '</p>' | |
}); | |
var meItems = new ViewSequence([profile]); // start with profile | |
var meScroll = new Scrollview(); | |
meSection.link(meScroll); | |
// populate the scrollviews | |
var sparks = firebase.child('sparks'); | |
// display the scrollviews when loaded | |
sparks.once('value', function() { | |
// rewind the pointers | |
while(homeItems.getPrevious()) homeItems = homeItems.getPrevious(); | |
while(discoverItems.getPrevious()) discoverItems = discoverItems.getPrevious(); | |
while(connectItems.getPrevious()) connectItems = connectItems.getPrevious(); | |
while(meItems.getPrevious()) meItems = meItems.getPrevious(); | |
// hook them up | |
homeScroll.sequenceFrom(homeItems); | |
discoverScroll.sequenceFrom(discoverItems); | |
connectScroll.sequenceFrom(connectItems); | |
meScroll.sequenceFrom(meItems); | |
}); | |
// update the scrollviews with data as they come in | |
var recentTimestampLimit = Date.now() - 90*86400000; // limit to 90 days | |
sparks.on('child_added', function(snapshot) { | |
var value = snapshot.val(); | |
if(value['timestamp'] > recentTimestampLimit) { | |
homeItems.unshift(new SparkItem({content: value})); | |
homeScroll.goToPreviousPage(); | |
} | |
if(value['author'] === myAuth['id']) { | |
meItems.splice(1, 0, new SparkItem({content: value})); | |
meScroll.goToPreviousPage(); | |
} | |
if(value['content'].indexOf('@') >= 0) { | |
connectItems.unshift(new SparkItem({content: value})); | |
connectScroll.goToPreviousPage(); | |
} | |
if(value['content'].indexOf('#') >= 0) { | |
discoverItems.unshift(new SparkItem({content: value})); | |
discoverScroll.goToPreviousPage(); | |
} | |
}); | |
// start on the home section | |
myApp.select('home'); | |
// signup button | |
var Modifier = require('famous/Modifier'); | |
var Matrix = require('famous/Matrix'); | |
var signupContext = FamousEngine.createContext(); | |
signupContext.link(new Modifier({origin: [0, 0], transform: Matrix.translate(10, 10), opacity: 0.7})).link(new Surface({ | |
size: [true, true], | |
classes: ['signup'], | |
content: '<a href="http://demo.famo.us/signup/">sign up</a>' | |
})); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
font-family: open_sansregular, sans-serif; | |
background-image: url("/tweetus/content/mback_small.png"); | |
-webkit-background-size: cover; | |
-moz-background-size: cover; | |
-o-background-size: cover; | |
background-size: cover; | |
vertical-align: middle; | |
text-align: center; | |
} | |
.header { | |
background-color: #4294ce; | |
color: #fff; | |
background: #76c5fb; /* Old browsers */ | |
background: -moz-linear-gradient(top, #76c5fb 0%, #60abe2 1%, #2978b1 99%, #18659d 100%); /* FF3.6+ */ | |
background: -webkit-linear-gradient(top, #76c5fb 0%,#60abe2 1%,#2978b1 99%,#18659d 100%); /* Chrome10+,Safari5.1+ */ | |
background: -o-linear-gradient(top, #76c5fb 0%,#60abe2 1%,#2978b1 99%,#18659d 100%); /* Opera 11.10+ */ | |
background: -ms-linear-gradient(top, #76c5fb 0%,#60abe2 1%,#2978b1 99%,#18659d 100%); /* IE10+ */ | |
background: linear-gradient(to bottom, #76c5fb 0%,#60abe2 1%,#2978b1 99%,#18659d 100%); /* W3C */ | |
font-family: open_sansbold, sans-serif; | |
font-size: 18px; | |
line-height: 26px; | |
padding: 10px; | |
text-align: center; | |
text-shadow: rgba(0,0,0,0.75) 0px -1px 0px; | |
z-index: 1; | |
} | |
.header .bird { | |
font-family: entypo-social; | |
font-size: 60px; | |
color: hsl(207,75%,70%); | |
} | |
.header .icon { | |
display: block; | |
height: 100%; | |
top: 0px; | |
right: 0px; | |
letter-spacing: 10px; | |
position: absolute; | |
font-size: 46px; | |
line-height: 46px; | |
font-family: 'entypo'; | |
} | |
.navigation { | |
padding: 3px; | |
font-family: open_sansbold; | |
font-size: 10px; | |
line-height: 10px; | |
text-align: center; | |
vertical-align: middle; | |
text-shadow: rgba(0,0,0,0.75) 0px -1px 0px; | |
z-index: 1; | |
} | |
.navigation.off { | |
color: #999; | |
background: #000000; /* Old browsers */ | |
background: -moz-linear-gradient(top, #000000 0%, #595959 1%, #383838 4%, #101010 98%, #000000 100%); /* FF3.6+ */ | |
background: -webkit-linear-gradient(top, #000000 0%,#595959 1%,#383838 4%,#101010 98%,#000000 100%); /* Chrome10+,Safari5.1+ */ | |
background: -o-linear-gradient(top, #000000 0%,#595959 1%,#383838 4%,#101010 98%,#000000 100%); /* Opera 11.10+ */ | |
background: -ms-linear-gradient(top, #000000 0%,#595959 1%,#383838 4%,#101010 98%,#000000 100%); /* IE10+ */ | |
background: linear-gradient(to bottom, #000000 0%,#595959 1%,#383838 4%,#101010 98%,#000000 100%); /* W3C */ | |
} | |
.navigation.on { | |
color: #fff; | |
background: #212121; /* Old browsers */ | |
background: -moz-linear-gradient(top, #212121 0%, #0f0f0f 100%); /* FF3.6+ */ | |
background: -webkit-linear-gradient(top, #212121 0%,#0f0f0f 100%); /* Chrome10+,Safari5.1+ */ | |
background: -o-linear-gradient(top, #212121 0%,#0f0f0f 100%); /* Opera 11.10+ */ | |
background: -ms-linear-gradient(top, #212121 0%,#0f0f0f 100%); /* IE10+ */ | |
background: linear-gradient(to bottom, #212121 0%,#0f0f0f 100%); /* W3C */ | |
box-shadow: #000 0px 0px 3px 2px inset; | |
} | |
.navigation .icon { | |
display: inline-block; | |
font-size: 26px; | |
line-height: 30px; | |
} | |
.navigation .icon .entypo { | |
font-family: entypo; | |
font-size: 48px; | |
line-height: 26px; | |
vertical-align: bottom; | |
} | |
.navigation.on .icon { | |
color: hsl(207, 75%, 65%); | |
} | |
.tweet { | |
background-color: #ffffff; | |
padding: 10px; | |
border-top: 1px solid #eee; | |
font-size: 12px; | |
text-align: left; | |
} | |
p { | |
padding: 0px; | |
margin: 0px; | |
border: 0px; | |
} | |
.tweet .icon { | |
float: left; | |
margin-right: 10px; | |
} | |
.tweet .source { | |
color: #000000; | |
font-family: open_sansbold; | |
text-align: left; | |
} | |
.tweet .source .screenname { | |
color: #909090; | |
font-family: open_sansregular; | |
} | |
.tweet .text { | |
color: #000000; | |
text-align: left; | |
text-overflow:ellipsis; | |
overflow: hidden; | |
} | |
.tweet .time { | |
color: #909090; | |
float: right; | |
} | |
.profile { | |
color: #fff; | |
background: #686868; /* Old browsers */ | |
background: -moz-radial-gradient(center, ellipse cover, #686868 0%, #141414 100%); /* FF3.6+ */ | |
background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%,#686868), color-stop(100%,#141414)); /* Chrome,Safari4+ */ | |
background: -webkit-radial-gradient(center, ellipse cover, #686868 0%,#141414 100%); /* Chrome10+,Safari5.1+ */ | |
background: -o-radial-gradient(center, ellipse cover, #686868 0%,#141414 100%); /* Opera 12+ */ | |
background: -ms-radial-gradient(center, ellipse cover, #686868 0%,#141414 100%); /* IE10+ */ | |
background: radial-gradient(ellipse at center, #686868 0%,#141414 100%); /* W3C */ | |
font-family: open_sansregular, sans-serif; | |
font-size: 18px; | |
line-height: 26px; | |
padding: 20px; | |
text-align: center; | |
text-shadow: rgba(0,0,0,0.75) 0px -1px 0px; | |
} | |
.profile img { | |
border: 5px solid #fff; | |
width: 80px; | |
height: 80px; | |
border-radius: 5px; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment