From 360f9ddbd72591af60751120eee1626ea2465c78 Mon Sep 17 00:00:00 2001 From: jlukic Date: Tue, 10 Mar 2015 12:47:21 -0400 Subject: [PATCH] Simplify visibility/sticky to use .load instead of worrying about each image loading --- RELEASE-NOTES.md | 2 + src/definitions/behaviors/visibility.js | 89 ++++++++++++++++++------- src/definitions/modules/sticky.js | 39 ++++++----- 3 files changed, 91 insertions(+), 39 deletions(-) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 8a5149fae..901e45edf 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -4,6 +4,8 @@ **Enhancements** - **Grid** - `equal height` and `equal width` now work without `row` wrappers +- **Visibility/Sticky** - Visibility now uses pub/sub pattern to greatly improve scroll performance when attaching multiple events +- **Visibility/Sticky** - Visibility and sticky now refresh automatically after page content loading to deal with changes in position from images loading **Bugs** - **All Modules** - Performance logging now delays 500ms instead of 100ms for console logging to ensure all logs are captured in one group diff --git a/src/definitions/behaviors/visibility.js b/src/definitions/behaviors/visibility.js index 1060c0f3c..d2b62658a 100644 --- a/src/definitions/behaviors/visibility.js +++ b/src/definitions/behaviors/visibility.js @@ -44,7 +44,6 @@ $.fn.visibility = function(parameters) { $window = $(window), $module = $(this), $context = $(settings.context), - $images = $module.find('img'), selector = $module.selector || '', instance = $module.data(moduleNamespace), @@ -100,8 +99,8 @@ $.fn.visibility = function(parameters) { .off(eventNamespace) .removeData(moduleNamespace) ; - $window.off('resize' + eventNamespace, module.event.refresh); - $context.off('scroll' + eventNamespace, module.event.scroll); + $window.off(eventNamespace); + $context.off('scrollchange'); }, observeChanges: function() { @@ -125,17 +124,19 @@ $.fn.visibility = function(parameters) { events: function() { module.verbose('Binding visibility events to scroll and resize'); $window + .on('load' + eventNamespace, module.event.refresh) .on('resize' + eventNamespace, module.event.refresh) ; + // pub/sub pattern $context + .off('scroll' + eventNamespace) .on('scroll' + eventNamespace, module.event.scroll) + .on('scrollchange' + eventNamespace, module.event.scrollchange) ; - if($images.length > 0) { - module.bind.imageLoad(); - } }, imageLoad: function() { var + $images = $module.find('img'), imageCount = $images.length, index = imageCount, loadedCount = 0, @@ -150,17 +151,19 @@ $.fn.visibility = function(parameters) { } } ; - $images - .each(function() { - images.push( $(this).attr('src') ); - }) - ; - while(index--) { - cacheImage = document.createElement('img'); - cacheImage.onload = handleLoad; - cacheImage.onerror = handleLoad; - cacheImage.src = images[index]; - cache.push(cacheImage); + if(imageCount > 0) { + $images + .each(function() { + images.push( $(this).attr('src') ); + }) + ; + while(index--) { + cacheImage = document.createElement('img'); + cacheImage.onload = handleLoad; + cacheImage.onerror = handleLoad; + cacheImage.src = images[index]; + cache.push(cacheImage); + } } } }, @@ -169,19 +172,50 @@ $.fn.visibility = function(parameters) { refresh: function() { requestAnimationFrame(module.refresh); }, + // published event scroll: function() { - module.verbose('Scroll position changed'); if(settings.throttle) { clearTimeout(module.timer); module.timer = setTimeout(function() { - module.checkVisibility(); + $context.trigger('scrollchange' + eventNamespace, [ $context.scrollTop() ]); }, settings.throttle); } else { requestAnimationFrame(function() { - module.checkVisibility(); + $context.trigger('scrollchange' + eventNamespace, [ $context.scrollTop() ]); }); } + }, + // subscribed event + scrollchange: function(event, scrollPosition) { + module.checkVisibility(scrollPosition); + }, + }, + + precache: function(images, callback) { + if (!(images instanceof Array)) { + images = [images]; + } + var + imagesLength = images.length, + loadedCounter = 0, + cache = [], + cacheImage = document.createElement('img'), + handleLoad = function() { + loadedCounter++; + if (loadedCounter >= images.length) { + if ($.isFunction(callback)) { + callback(); + } + } + } + ; + while (imagesLength--) { + cacheImage = document.createElement('img'); + cacheImage.onload = handleLoad; + cacheImage.onerror = handleLoad; + cacheImage.src = images[imagesLength]; + cache.push(cacheImage); } }, @@ -291,6 +325,7 @@ $.fn.visibility = function(parameters) { module.reset(); module.save.position(); module.checkVisibility(); + settings.onRefresh.call(element); }, @@ -302,11 +337,14 @@ $.fn.visibility = function(parameters) { } }, - checkVisibility: function() { + checkVisibility: function(scrollPosition) { module.verbose('Checking visibility of element', module.cache.element); if( module.is.visible() ) { + // save scroll position + module.save.scroll(scrollPosition); + // update calculations derived from scroll module.save.calculations(); @@ -616,7 +654,6 @@ $.fn.visibility = function(parameters) { save: { calculations: function() { module.verbose('Saving all calculations necessary to determine positioning'); - module.save.scroll(); module.save.direction(); module.save.screenCalculations(); module.save.elementCalculations(); @@ -629,8 +666,9 @@ $.fn.visibility = function(parameters) { } } }, - scroll: function() { - module.cache.scroll = $context.scrollTop() + settings.offset; + scroll: function(scrollPosition) { + scrollPosition = scrollPosition || $context.scrollTop(); + module.cache.scroll = scrollPosition; }, direction: function() { var @@ -975,6 +1013,9 @@ $.fn.visibility.settings = { // whether to use mutation observers to follow changes observeChanges : true, + // whether to refresh calculations after all page images load + refreshOnLoad : true, + // callback should only occur one time once : true, diff --git a/src/definitions/modules/sticky.js b/src/definitions/modules/sticky.js index 484fa35fa..eee292d27 100644 --- a/src/definitions/modules/sticky.js +++ b/src/definitions/modules/sticky.js @@ -92,8 +92,8 @@ $.fn.sticky = function(parameters) { if(observer) { observer.disconnect(); } - $window.off('resize' + eventNamespace, module.event.resize); - $scroll.off('scroll' + eventNamespace, module.event.scroll); + $window.off('resize' + eventNamespace); + $scroll.off('scrollchange' + eventNamespace); $module.removeData(moduleNamespace); }, @@ -147,23 +147,31 @@ $.fn.sticky = function(parameters) { bind: { events: function() { - $window.on('resize' + eventNamespace, module.event.resize); - $scroll.on('scroll' + eventNamespace, module.event.scroll); + $window + .on('load' + eventNamespace, module.event.refresh) + .on('resize' + eventNamespace, module.event.refresh) + ; + // pub/sub pattern + $scroll + .off('scroll' + eventNamespace) + .on('scroll' + eventNamespace, module.event.scroll) + .on('scrollchange' + eventNamespace, module.event.scrollchange) + ; } }, event: { - resize: function() { - requestAnimationFrame(function() { - module.refresh(); - module.stick(); - }); + refresh: function() { + requestAnimationFrame(module.refresh); }, scroll: function() { requestAnimationFrame(function() { - module.stick(); - settings.onScroll.call(element); + $scroll.trigger('scrollchange' + eventNamespace, $scroll.scrollTop() ); }); + }, + scrollchange: function(event, scrollPosition) { + module.stick(scrollPosition); + settings.onScroll.call(element); } }, @@ -268,6 +276,7 @@ $.fn.sticky = function(parameters) { : Math.abs(parseInt($module.css('bottom'), 10)) || 0 ; }, + elementScroll: function(scroll) { scroll = scroll || $scroll.scrollTop(); var @@ -369,8 +378,9 @@ $.fn.sticky = function(parameters) { } }, - stick: function() { + stick: function(scroll) { var + cachedPosition = scroll || $scroll.scrollTop(), cache = module.cache, fits = cache.fits, element = cache.element, @@ -380,8 +390,8 @@ $.fn.sticky = function(parameters) { ? settings.bottomOffset : settings.offset, scroll = { - top : $scroll.scrollTop() + offset, - bottom : $scroll.scrollTop() + offset + window.height + top : cachedPosition + offset, + bottom : cachedPosition + offset + window.height }, direction = module.get.direction(scroll.top), elementScroll = module.get.elementScroll(scroll.top), @@ -398,7 +408,6 @@ $.fn.sticky = function(parameters) { if( module.is.initialPosition() ) { if(scroll.top >= context.bottom) { - console.log(scroll.top, context.bottom); module.debug('Element bottom of container'); module.bindBottom(); }