Skip to content

Instantly share code, notes, and snippets.

@hatsumatsu
Last active May 17, 2016 16:21
Show Gist options
  • Save hatsumatsu/4f2eea5af08f4554d4e0 to your computer and use it in GitHub Desktop.
Save hatsumatsu/4f2eea5af08f4554d4e0 to your computer and use it in GitHub Desktop.
JS viewport module for Port F
// module viewport
var viewport = ( function() {
var settings = {
width: 0,
height: 0,
documentHeight: 0,
nowLoop: Date.now(),
fps: ( 1000 / 60 ),
// scrolling
scrollTop: -1,
_scrollTop: -1,
scrollDetectionMode: 'scrollEvent', // 'scrollEvent' or 'requestAnimationFrame'
nowScroll: Date.now(),
scrollFactor: -1,
isScrolledToTop: false,
isScrolledToBottom: false,
scrollTopOffset: 20,
scrollBottomOffset: 20,
isScrolledToFirstScreen: false,
isScrolledToLastScreen: false,
scrollToOffset: 0,
scrollToSpeed: 2
};
var init = function() {
debuglog( 'viewport.init()' );
settings.element = $( window );
onResizeFinish();
bindEventHandlers();
onScroll();
onLoop();
}
var bindEventHandlers = function() {
// throttle resize event
settings.element.on( 'resize', function() {
if( settings.resizeDelay ) {
clearTimeout( settings.resizeDelay );
settings.resizeDelay = null;
} else {
$( 'html' ).addClass( 'resizing' );
$( document ).trigger( 'viewport/resize/start' );
}
settings.resizeDelay = setTimeout( function() {
$( 'html' ).removeClass( 'resizing' );
$( document ).trigger( 'viewport/resize/finish' );
settings.resizeDelay = null;
}, 500 );
} );
// scroll event
if( settings.scrollDetectionMode == 'scrollEvent' ) {
settings.element.on( 'scroll', function() {
var now = Date.now();
var elapsed = now - settings.nowScroll;
if( elapsed > settings.fps ) {
settings.nowScroll = now - ( elapsed % settings.fps );
settings.scrollTop = settings.element.scrollTop();
$( document ).trigger( 'viewport/scroll' );
}
} );
}
$( document )
.on( 'viewport/scroll', function() {
onScroll();
} )
.on( 'viewport/resize/finish', function() {
onResizeFinish();
} )
// top
.on( 'viewport/scroll/toTop', function() {
debuglog( 'scrolled to top' );
$( 'html' ).addClass( 'scrolled-to-top' );
} )
.on( 'viewport/scroll/fromTop', function() {
debuglog( 'scrolled from top' );
$( 'html' ).removeClass( 'scrolled-to-top' );
} )
// bottom
.on( 'viewport/scroll/toBottom', function() {
debuglog( 'scrolled to bottom' );
$( 'html' ).addClass( 'scrolled-to-bottom' );
} )
.on( 'viewport/scroll/fromBottom', function() {
debuglog( 'scrolled from bottom' );
$( 'html' ).removeClass( 'scrolled-to-bottom' );
} )
// first screen
.on( 'viewport/scroll/toFirstScreen', function() {
debuglog( 'scrolled to first screen' );
$( 'html' ).addClass( 'scrolled-to-first-screen' );
} )
.on( 'viewport/scroll/fromFirstScreen', function() {
debuglog( 'scrolled from first screen' );
$( 'html' ).removeClass( 'scrolled-to-first-screen' );
} )
// last screen
.on( 'viewport/scroll/toLastScreen', function() {
debuglog( 'scrolled to last screen' );
$( 'html' ).addClass( 'scrolled-to-last-screen' );
} )
.on( 'viewport/scroll/fromLastScreen', function() {
debuglog( 'scrolled from last screen' );
$( 'html' ).removeClass( 'scrolled-to-last-screen' );
} );
}
/**
* requestAnimationFrame loop throttled at 60fps
*/
var onLoop = function() {
requestAnimationFrame( onLoop );
var now = Date.now();
var elapsed = now - settings.nowLoop;
// the actual 'loop'
if( elapsed > settings.fps ) {
settings.nowLoop = now - ( elapsed % settings.fps );
$( document ).trigger( 'viewport/loop' );
// scrollTop
if( settings.scrollDetectionMode == 'requestAnimationFrame' ) {
settings._scrollTop = settings.scrollTop;
settings.scrollTop = settings.element.scrollTop();
if( settings.scrollTop != settings._scrollTop ) {
$( document ).trigger( 'viewport/scroll' );
}
}
}
}
var onResizeFinish = function() {
debuglog( 'viewport.onResizeFinish()' );
settings.width = settings.element.width();
settings.height = settings.element.height();
settings.scrollTop = settings.element.scrollTop();
settings.documentHeight = $( 'html' ).outerHeight();
}
var onScroll = function() {
debuglog( 'viewport.onScroll()' );
settings.scrollFactor = settings.scrollTop / ( settings.height - settings.documentHeight ) * -1;
// top
if( settings.scrollTop > settings.scrollTopOffset ) {
if( settings.isScrolledToTop ) {
settings.isScrolledToTop = false;
$( document ).trigger( 'viewport/scroll/fromTop' );
}
}
if( settings.scrollTop < settings.scrollTopOffset ) {
if( !settings.isScrolledToTop ) {
settings.isScrolledToTop = true;
$( document ).trigger( 'viewport/scroll/toTop' );
}
}
// bottom
if( settings.scrollTop > settings.documentHeight - settings.height - settings.scrollBottomOffset ) {
if( !settings.isScrolledToBottom ) {
settings.isScrolledToBottom = true;
$( document ).trigger( 'viewport/scroll/toBottom' );
}
}
if( settings.scrollTop < settings.documentHeight - settings.height - settings.scrollBottomOffset ) {
if( settings.isScrolledToBottom ) {
settings.isScrolledToBottom = false;
$( document ).trigger( 'viewport/scroll/fromBottom' );
}
}
// first screen
if( settings.scrollTop < settings.height ) {
if( !settings.isScrolledToFirstScreen ) {
settings.isScrolledToFirstScreen = true;
$( document ).trigger( 'viewport/scroll/toFirstScreen' );
}
}
if( settings.scrollTop > settings.height ) {
if( settings.isScrolledToFirstScreen ) {
settings.isScrolledToFirstScreen = false;
$( document ).trigger( 'viewport/scroll/fromFirstScreen' );
}
}
// last screen
if( settings.scrollTop > settings.documentHeight - ( 2* settings.height ) ) {
if( !settings.isScrolledToLastScreen ) {
settings.isScrolledToLastScreen = true;
$( document ).trigger( 'viewport/scroll/toLastScreen' );
}
}
if( settings.scrollTop < settings.documentHeight - ( 2* settings.height ) ) {
if( settings.isScrolledToLastScreen ) {
settings.isScrolledToLastScreen = false;
$( document ).trigger( 'viewport/scroll/fromLastScreen' );
}
}
}
var scrollTo = function( target, offset, animate ) {
debuglog( 'viewport.scrollTo()' );
debuglog( target );
var top = 0;
// scroll to position
if( typeof target == 'number' ) {
top = target;
}
// scroll to id
if( typeof target == 'string' && $( '#' + target ).length > 0 ) {
top = parseInt( $( '#' + target ).offset().top );
}
// scroll to element
if( typeof target == 'object' && target.length > 0 ) {
top = parseInt( target.offset().top );
}
if( offset ) {
top = top + offset;
} else {
top = top + settings.scrollToOffset;
}
var distance = Math.floor( Math.abs( top - $( window ).scrollTop() ) );
var duration = Math.floor( distance / settings.scrollToSpeed );
if( animate ) {
$( 'html, body' ).animate( {
scrollTop: top
}, duration );
} else {
settings.element.scrollTop( top );
}
}
var getWidth = function() {
return settings.width;
}
var getHeight = function() {
return settings.height;
}
var getScrollTop = function() {
return settings.scrollTop;
}
var getScrollFactor = function() {
return settings.scrollFactor;
}
return {
init: function() { init(); },
scrollTo: function( target, offset, animate ) { scrollTo( target, offset, animate ) },
getWidth: function() { return getWidth() },
getHeight: function() { return getHeight() },
getScrollTop: function() { return getScrollTop() },
getScrollFactor: function() { return getScrollFactor() }
}
} )();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment