Created
April 28, 2015 16:02
-
-
Save knubie/92efd8f69d7d8bb109ca to your computer and use it in GitHub Desktop.
This file contains hidden or 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
# My attempt at generalizing a common feature among C&T sites. | |
class Transporter | |
scroll = if 'ontouchmove' of window then 'ontouchmove' else 'scroll' | |
scroll = 'scroll' | |
history.pushState ?= -> null | |
$window = $(window) | |
$document = $(document) | |
LOADER_HEIGHT = null | |
constructor: ({@content, @footer, @paths, @onPageLoad, @onPageChange}) -> | |
@index = 0 | |
@loading = no | |
@gettingPage = no | |
@pages = [] | |
@pages.push new Page $(@content), $(@footer) | |
@$loader = $('<div class="transporter_loader"></div>') | |
$('body').append @$loader.css 'visibility', 'hidden' | |
LOADER_HEIGHT = @$loader.height() | |
@$loader.remove().css 'visibility', '' | |
$('.site_nav_jump--secondary_menu').attr('data-index', @index) | |
$window.on scroll, => @onScroll() | |
onScroll: -> | |
if @isDesktop() | |
# Insert or remove loader. | |
@insertLoader() if not @loading and @footerAboveBottom() and @paths[@index]? | |
@removeLoader() if @loading and @footerBelowBottom() | |
# When scrolling up, activate the pevious Page. | |
if @pages.length > 1 and @index > 0 and $window.scrollTop() <= 0 | |
# Unfix the previous page so it scrolls with the window. | |
@previousPage().unfixWrapper() | |
@previousPage().$wrapper.show().css | |
'transform': "translateY(0)" | |
@currentPage().$content.css 'margin-top', '60px' | |
$window.scrollTop @previousPage().absoluteHeight() + LOADER_HEIGHT - $window.height() | |
@index-- | |
history.pushState {}, '', if @index is 0 then '/' else @paths[@index - 1] | |
@onPageChange?() | |
# When scrolling down, activate the next Page. | |
if @pages.length > (@index + 1) and | |
@scrollBottom() > @currentPage().absoluteHeight() + LOADER_HEIGHT | |
# Save the current scrollBottom | |
scrollBottom = @scrollBottom() | |
# Fix current page in place so new page scrolls over it. | |
@currentPage().fixWrapper() | |
# Push the new Page into place and add an opaque background. | |
@nextPage().$content.css | |
'background-color': 'white' | |
'margin-top': $window.height() - LOADER_HEIGHT | |
# Reset the scroll position by subtracting | |
# the height of the current Page. | |
$window.scrollTop \ | |
Math.max(scrollBottom - (@currentPage().absoluteHeight() + LOADER_HEIGHT), 1) | |
history.pushState({}, "", @paths[@index]) | |
@index++ | |
# Hide or show the previous page when it's covered or uncovered | |
# by the current page. | |
if @pages.length > 1 and @index > 0 | |
if $window.scrollTop() >= ($window.height() - LOADER_HEIGHT) | |
@previousPage().$wrapper.hide().css('visibility', 'hidden') | |
@onPageChange?() | |
else | |
@previousPage().$wrapper.show().css | |
'visibility': 'visible' | |
# Parallax effect. | |
'transform': "translateY(-#{$window.scrollTop()/2}px)" | |
# Insert new page. | |
if @footerInView() and not @gettingPage and @paths[@index]? | |
@gettingPage = yes | |
@insertLoader() if @isMobile() | |
@getPage @index, (html) => | |
$content = $(html).find(@content) | |
if @loading | |
if @isDesktop() | |
# If the user is scrolled up a little so that the loader is | |
# partially hidden, we need to compensate for that by | |
# adding negative margin to the bottom of the loader. | |
_distanceFromBottom = @distanceFromBottom() | |
@$loader.css('margin-bottom', Math.min((0 - _distanceFromBottom), 0)) | |
$content.css 'margin-top', '60px' | |
@loading = false | |
@currentPage().unfixFooter() | |
@$loader.last().animate | |
'height': _distanceFromBottom | |
, 500, => @removeLoader() | |
else # isMobile | |
@loading = false | |
@$loader.last().animate | |
'height': 0 | |
, 500, => @removeLoader() | |
# Create new Page object. | |
@pages.push new Page $content, @currentPage().$footer.clone(true) | |
# Append page to the site. | |
@currentPage().$wrapper.after @nextPage().$wrapper | |
# Perform init operations on new Page. | |
@onPageLoad? @nextPage().$content, html | |
@gettingPage = no | |
@index++ if @isMobile() | |
insertLoader: -> | |
if @isDesktop() | |
@currentPage().$footer.before @$loader | |
# Pad the bottom of the loader so it sits above the footer. | |
@$loader.css('margin-bottom', "#{@pages[@index].$footer.outerHeight()}px") | |
@currentPage().fixFooter() | |
else | |
@currentPage().$footer.after @$loader | |
@loading = yes | |
removeLoader: -> | |
@currentPage().unfixFooter() | |
@$loader.remove() | |
@$loader.css('height', '90px') | |
@loading = no | |
getPage: (index, callback) -> | |
$.ajax | |
type: 'POST' | |
url: @paths[index] | |
.done (html) -> | |
setTimeout -> | |
callback? html | |
, 1000 | |
.fail -> console.log 'page injection failed' | |
# Helpers | |
scrollBottom: -> # return Number | |
$window.scrollTop() + $window.height() | |
previousPage: -> # return {Page} | |
@pages[@index - 1] | |
currentPage: -> # return {Page} | |
@pages[@index] | |
nextPage: -> # return {Page} | |
@pages[@index + 1] | |
footerInView: -> # return Boolean | |
footerTop = $document.height() - $(@footer).last().outerHeight() | |
@scrollBottom() >= footerTop | |
footerAboveBottom: -> # return Boolean | |
@scrollBottom() >= $document.height() | |
footerBelowBottom: -> # return Boolean | |
@scrollBottom() < $document.height() - LOADER_HEIGHT | |
distanceFromBottom: -> # return Number | |
$document.height() - @scrollBottom() | |
isMobile: -> # return Boolean | |
'ontouchmove' of window | |
#$window.width() < 768 | |
isDesktop: -> # return Boolean | |
$window.width() >= 768 | |
inView: (page) -> # return Boolean | |
@scrollBottom() > page.wrapperTop > $(window).scrollTop() - page.$wrapper.height() | |
class Page | |
LOADER_HEIGHT = 90 | |
$window = $(window) | |
constructor: (@$content, @$footer) -> | |
@$wrapper = @$content.add(@$footer).wrapAll('<div class="transporter-wrapper" />').parent() | |
@wrapperTop = @$wrapper.position().top | |
fixFooter: -> | |
@$footer.css | |
'position': 'fixed' | |
'bottom': '0' | |
'width': "#{$window.width() - @$footer.position().left}px" | |
#'width': "calc(100vw - #{@$footer.position().left}px)" | |
'z-index': '24' # One less than the sticky nav on mobile. | |
unfixFooter: -> | |
@$footer.css | |
'position': 'relative' | |
'bottom': '0' | |
'width': '' | |
'z-index': '100' | |
fixWrapper: -> | |
@$wrapper.css | |
'position': 'fixed' | |
'bottom': LOADER_HEIGHT | |
'z-index': -1 | |
'width': "#{$window.width() - @$wrapper.position().left}px" | |
#'width': "calc(100vw - #{@$wrapper.position().left}px)" | |
unfixWrapper: -> | |
@$wrapper.css | |
'position': 'relative' | |
'bottom': '' | |
'z-index': '' | |
'width': '' | |
absoluteHeight: -> | |
@$content.outerHeight(true) + @$footer.outerHeight(true) + @wrapperTop | |
new Transporter | |
content: '.content' | |
footer: '.site_footer_container' | |
paths: paths | |
onPageLoad: ($content, html) -> | |
# Do stuff on new page load. | |
onPageChange: -> | |
# Do some stuff on page change. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment