git clone https://github.com/nclud/2011.beercamp.com.git
/* level 2 -> scale = 1 / ( 3^1 ) = 1/3 */
#idea-examples .zoom .section2 {
-webkit-transform: scale(0.333);
-moz-transform: scale(0.333);
-ms-transform: scale(0.333);
-o-transform: scale(0.333);
transform: scale(0.333);
}
/* level 3 -> scale = 1 / ( 3^2 ) = 1/9 */
#idea-examples .zoom .section3 {
-webkit-transform: scale(0.111);
-moz-transform: scale(0.111);
-ms-transform: scale(0.111);
-o-transform: scale(0.111);
transform: scale(0.111);
}
/* scale = 3^1 = 3 */
#idea-examples.view2 .zoom ul {
-webkit-transform: scale(3);
-moz-transform: scale(3);
-ms-transform: scale(3);
-o-transform: scale(3);
transform: scale(3);
}
/* scale = 3^2 = 9 */
#idea-examples.view3 .zoom ul {
-webkit-transform: scale(9);
-moz-transform: scale(9);
-ms-transform: scale(9);
-o-transform: scale(9);
transform: scale(9);
}
Design Nobility Pyramid by Frank Chimero
nikebetterworld.com by Ian Coyle & Duane King
See css/style.css
All content is position: fixed
.
.csstransforms #wrap { position: fixed; }
Transparent proxy element is the only thing that scrolls.
.csstransforms.no-touch #scroll-proxy { height: 5400px; }
See js/scripts.js
this
will refer to constructor’s instance. Has methods and propertiesfunction Beercamper() {
// properties
this.scrolled = 0;
this.currentLevel = 0;
this.levels = 7;
this.distance3d = 1000;
this.levelGuide = {
'#intro' : 0,
'#featuring' : 1,
'#sponsorz' : 2,
'#flip-cup' : 3,
'#teams' : 4
};
// which method should be used to return CSS transform styles
this.getScrollTransform = Modernizr.csstransforms3d ?
this.getScroll3DTransform : this.getScroll2DTransform;
// bind constructor to window.scroll event
if ( Modernizr.csstransforms ) {
window.addEventListener( 'scroll', this, false);
}
}
// enables constructor to be used within event listener
// like obj.addEventListener( eventName, this, false )
Beercamper.prototype.handleEvent = function( event ) {
if ( this[event.type] ) {
this[event.type](event);
}
};
Rather than bind inline or add anonymous functions to make our object call context preserved, we can simply add an handleEvent method to whatever object and pass it as EventListener.
Beercamper.scroll
this.scrolled
is calculated as decimal (0 to 1)this.scrolled
value is used within transformScroll
methodBeercamper.prototype.scroll = function( event ) {
// normalize scroll value from 0 to 1
this.scrolled = this.$window.scrollTop() /
( this.$document.height() - this.$window.height() );
this.transformScroll( this.scrolled );
// change current selection on nav
this.currentLevel = Math.round( this.scrolled * (this.levels-1) );
if ( this.currentLevel !== this.previousLevel && this.$nav ) {
this.$nav.find('.current').removeClass('current');
if ( this.currentLevel < 5 ) {
this.$nav.children().eq( this.currentLevel ).addClass('current');
}
this.previousLevel = this.currentLevel;
}
};
// where the magic happens
// applies transform to content from position of scroll
Beercamper.prototype.transformScroll = function( scroll ) {
this.$content.css( this.getScrollTransform( scroll ) );
};
Back in initial Beercamper
constructor expression, getScrollTransform
method was set to either getScroll3DTransform
or getScroll2DTransform
, depending on browser’s CSS 3D transform support.
Both methods return the transform style that will be applied to the content’s container – this.$content
Beercamper.prototype.getScroll2DTransform = function( scroll ) {
// 2D scale is exponential
var scale = Math.pow( 3, scroll * (this.levels - 1) ),
prop = 'scale(' + scale + ')',
style = {
WebkitTransform : prop,
MozTransform : prop,
OTransform : prop,
transform : prop
};
return style;
};
Beercamper.prototype.getScroll3DTransform = function( scroll ) {
var z = ( scroll * (this.levels - 1) * this.distance3d ),
style;
style = {
WebkitTransform : 'translate3d( 0, 0, ' + z + 'px )'
};
return style;
};
All that’s left is to initialize the constructor.
// BeerCamp '11 Global object
// initialize Beercamper
var BCXI = new Beercamper();
// check if browser is iOS -> iPhone / iPad / iPod Touch
BCXI.isIOS = !!('createTouch' in document);
$(function(){
BCXI.$content = $('#content');
BCXI.$nav = $('#nav');
var $body = $('body'),
iOSclass = BCXI.isIOS ? 'ios' : 'no-ios';
$body.addClass( iOSclass );
});
position: fixed
doesn’t work in iOSSolution: Have iOS users use buttons for navigation
Disable proxy scroll in CSS
.csstransforms.no-touch .sign-up #scroll-proxy { height: 0; }
Beercamper
constructorif ( Modernizr.csstransforms ) {
$('.page-nav').each(function(){
this.addEventListener( 'click', BCXI, false );
});
}
Meanwhile, back in Beercamper
…
// handle click events
Beercamper.prototype.click = function( event ) {
// get scroll based on href of clicked nav item
var hash = event.target.hash || event.target.parentNode.hash,
targetLevel = this.levelGuide[ hash ],
scroll = targetLevel / (this.levels-1);
// turn on transitions and add event listeners for its end
if ( Modernizr.csstransitions ) {
this.$content.addClass('transitions-on');
this.$content[0].addEventListener( 'webkitTransitionEnd', this, false );
this.$content[0].addEventListener( 'oTransitionEnd', this, false );
this.$content[0].addEventListener( 'transitionend', this, false );
}
// set scrollbar position
// this will trigger window scroll event -> Beercamper.prototype.scroll
this.$window.scrollTop( scroll * ( this.$document.height() - this.$window.height() ) );
// iOS doesn't have scrollbar, so we have to manually trigger it
if ( this.isIOS ) {
this.transformScroll( scroll );
}
event.preventDefault();
};
Related CSS
.csstransforms #content.transitions-on {
-webkit-transition: -webkit-transform 1s;
-moz-transition: -moz-transform 1s;
-o-transition: -o-transform 1s;
transition: transform 1s;
}
Beercamper.prototype.webkitTransitionEnd = function( event ) {
this.transitionEnded( event );
};
Beercamper.prototype.transitionend = function( event ) {
this.transitionEnded( event );
};
Beercamper.prototype.oTransitionEnd = function( event ) {
this.transitionEnded( event );
};
// disables transition after nav click
Beercamper.prototype.transitionEnded = function( event ) {
this.$content.removeClass('transitions-on');
this.$content[0].removeEventListener( 'webkitTransitionEnd', this, false );
this.$content[0].removeEventListener( 'transitionend', this, false );
this.$content[0].removeEventListener( 'oTransitionEnd', this, false );
};
Users can scroll as they normally would
No reliance on button navigation
Refresh page - transformScroll automatically takes effect
Progressively enhanced awesome
Also used for this Colophon