Last active
November 20, 2017 17:50
-
-
Save codfish/4f7753423570847bf3f9d96912bd9e70 to your computer and use it in GitHub Desktop.
AngularJS 1.x Slick Carousel Directive
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
<div class="slick-slider clearfix" | |
data-slick-carousel | |
data-slides="gallery.slides" | |
data-interstitial="gallery.interstitial" | |
data-settings="{}" | |
data-active="gallery.active" | |
data-onchange="gallery.onAfterChange" | |
data-count="gallery.slides.length" | |
data-interstitial-link="gallery.interstitial.link" | |
data-autoplay="true" | |
data-responsive="false" | |
data-state="ready"></div> |
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
/** | |
* Slick Carousel Controller | |
* | |
* @param {Scope} $scope ng scope of the directive | |
* @param {jQuery} $element jquery object containing the element | |
* @param {object} attrs obj containing the html attributes on the element | |
*/ | |
function SlickCarouselController($scope, $element, $attrs) { | |
this.slides = $scope.slides; | |
this.interstitial = $scope.interstitial; | |
} | |
angular | |
.module('slickCarousel') | |
.controller('SlickCarouselController', SlickCarouselController); |
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
/** | |
* jQuery Slick Carousel Directive | |
* | |
* @see http://kenwheeler.github.io/slick/ | |
* @see http://stackoverflow.com/a/24523463 | |
* @ngInject | |
*/ | |
function SlickCarouselDirective($window, $timeout, debounce) { | |
var _slickState = 'ready', | |
_currentSlideIndex = null, | |
_interstitialIsShown = false, | |
_interstitialLink = true, | |
_initialSlide = true; | |
/** | |
* Autoplay Handler | |
* | |
* This method handles the state of the carousel and any event | |
* logic for the playing and pausing of the carousel. | |
* | |
* @return {[type]} [description] | |
*/ | |
var _autoPlayHandler = function() { | |
var $element = this; | |
// add hover class for any older browsers | |
$element.on({ | |
mouseenter: function() { $element.addClass('hover'); }, | |
mouseleave: function() { $element.removeClass('hover'); } | |
}); | |
// pause & play functionality | |
$element.on('click', function(event) { | |
if ($(event.target).hasClass('nav-item')) { | |
// user clicked on a nav item, let them take over | |
$element.slickPause(); | |
_slickState = 'paused'; | |
return; | |
} | |
switch(_slickState) { | |
case 'ready': | |
case 'paused': | |
_slickState = 'playing'; | |
$element.slickPlay() | |
.addClass('playing') | |
.removeClass('paused'); | |
break; | |
case 'playing': | |
_slickState = 'paused'; | |
$element.slickPause() | |
.addClass('playing') | |
.removeClass('paused'); | |
break; | |
} | |
}); | |
}; | |
/** | |
* Preload slide images | |
* | |
* Custom helper fn to preload images. Loads images of the slides | |
* in proximity to the active slide in order to speed up transitions. | |
* Dont totally rely on slicks' built in lazy loading. | |
* | |
* @param {int} idx active slide index | |
*/ | |
var _preload = function(idx) { | |
var $element = this, | |
$slides = $element.find('.slick-slide'), | |
$closeSlides = $slides.slice(idx > 0 ? idx - 1 : 0, idx + 3), // n - 1, n + 2 | |
$images = $closeSlides.find('img'), | |
$image = null; | |
// iterate through all flipbook images nearest the active one | |
// and give them a source based on their 'data-lazy' attribute | |
for (var i = 0; i < $images.length; i++) { | |
$image = $($images[i]); | |
if ($image.length) { | |
if ((!$image.attr('src')) && ($image.data('lazy') !== '')) { | |
$image.attr('src', $image.data('lazy')); | |
$image.removeClass('slick-loading'); | |
} | |
} | |
} | |
}; | |
/** | |
* Adjust the height of the carousel | |
* | |
* Setting the height of the carousel explicitly to remove | |
* any jankyness caused by lazyloading the images. | |
*/ | |
var _adjustCarouselHeight = debounce(500, function() { | |
var $element = this; | |
$element.css('height', $element.find('img[src]:first').outerHeight(true)); | |
}); | |
/** | |
* Allow arrow keyboard keys to paginate the carousel | |
* | |
* @param {Event} event keydown event object | |
*/ | |
var _keyHandler = function(event) { | |
var key = event.keyCode, | |
$element = this; | |
if (key === 37) { | |
$element.slickPrev(); | |
} else if (key === 39) { | |
$element.slickNext(); | |
} | |
}; | |
$window.onkeydown = _keyHandler; | |
/** | |
* Method to fire once the first image is fully loaded | |
* | |
* @param {boolean} isResponsive if the carousel is responsive | |
*/ | |
var _imagesLoaded = function(isResponsive) { | |
var $element = this; | |
$element.addClass('slick-images-visible'); | |
if (isResponsive) { | |
_adjustCarouselHeight(); | |
angular.element($window).on('resize orientationchange', _adjustCarouselHeight); | |
} | |
}; | |
/** | |
* Check to see if user is on the last slide and has pressed next. | |
* If so, add a class to the carousel in order to display the interstitial | |
* | |
* @param {int} idx current slide index | |
* @param {int} numSlides total number of slides | |
*/ | |
var _checkInterstitial = function(idx, numSlides) { | |
var $element = this; | |
if (!_interstitialLink) { | |
return; | |
} | |
if (_currentSlideIndex === idx && _currentSlideIndex === (numSlides - 1)) { | |
if (_interstitialIsShown) { | |
// already showing, then follow the interstitial link | |
$window.location.href = _interstitialLink; | |
} else { | |
_interstitialIsShown = true; | |
$element.addClass('interstitial-active'); | |
$element.slickPause(); | |
} | |
} else if (_interstitialIsShown) { | |
_interstitialIsShown = false; | |
$element.removeClass('interstitial-active'); | |
} | |
}; | |
/** | |
* Link function for directive | |
* | |
* @param {Scope} $scope ng scope of the directive | |
* @param {jQuery} $element jquery object containing the element | |
* @param {object} attrs obj containing the html attributes on the element | |
*/ | |
function link($scope, $element, attrs) { | |
var activeIndex = $scope.active ? $scope.active - 1 : 0, | |
_responsive = $scope.responsive === 'true', | |
_autoplay = $scope.autoplay === 'true', | |
_numSlides = $scope.count, | |
_customOnChangeFn = $scope.onchange || function() {}, | |
settings = $scope.settings, | |
defaults = {}, | |
options = {}; | |
_autoPlayHandler = angular.element.proxy(_autoPlayHandler, $element); | |
_preload = angular.element.proxy(_preload, $element); | |
_adjustCarouselHeight = angular.element.proxy(_adjustCarouselHeight, $element); | |
_keyHandler = angular.element.proxy(_keyHandler, $element); | |
_imagesLoaded = angular.element.proxy(_imagesLoaded, $element); | |
_checkInterstitial = angular.element.proxy(_checkInterstitial, $element); | |
_interstitialLink = $scope.interstitialLink; | |
$timeout(function() { | |
if (_autoplay) { | |
_autoPlayHandler(); | |
} | |
/** | |
* Callback fn after slide changes | |
* | |
* @todo | |
* debounce so that if user changes slides very quickly | |
* it wont kill the page, and it will only fire once. Execution | |
* should occur on the frontend of the timeout! | |
* | |
* @param {Slick} elem slick carousel element | |
* @param {int} idx current slide index | |
*/ | |
function _onAfterChange(elem, idx) { | |
_checkInterstitial(idx, _numSlides); | |
// check if slide didn't actually change | |
if (_currentSlideIndex === idx) { | |
return; | |
} | |
_currentSlideIndex = idx; | |
_preload(idx); | |
// the _onAfterChange() callback is fired on pageload because we want to | |
// make sure the proper slide is shown. We want to prevent any callbacks | |
// from firing in this scenario. Callbacks should only fire on user-initiated slides. | |
if (_initialSlide) { | |
_initialSlide = false; | |
// Add class to slider once the active image is loaded. | |
// This is to prevent jumpiness of the carousel while initially loading. | |
// @see http://goo.gl/6SvwBz | |
var $activeImage = $element.find('img').eq(idx); | |
if ($activeImage[0].complete) { | |
_imagesLoaded(_responsive); | |
} else { | |
$activeImage.on('load', function() { | |
_imagesLoaded(_responsive); | |
}); | |
} | |
return; | |
} | |
_customOnChangeFn(idx); | |
} | |
// set defaults for CAD's slick carousel | |
defaults = { | |
accessibility: false, | |
autoplaySpeed: 4500, | |
autoplayChangeDirection: false, | |
cssTransitions: false, | |
customPaging: function(slider, i) { | |
return '<button class="nav-item" type="button" data-role="none">' + (i + 1) + '</button>'; | |
}, | |
dots: true, | |
draggable: true, | |
infinite: false, | |
lazyAnimation: false, // COD PR...@see https://github.com/kenwheeler/slick/pull/626 | |
lazyLoad: 'ondemand', | |
nextArrow: '<span class="slick-next"><span class="irg-RightArrow nav-item">Next</span></span>', | |
prevArrow: '<span class="slick-prev"><span class="irg-LeftArrow nav-item">Previous</span></span>', | |
onAfterChange: _onAfterChange, | |
pauseOnHover: false, | |
slidesToShow: 1, | |
waitForAnimate: false | |
}; | |
// merge defaults with user-set options and initialize slick carousel | |
options = angular.extend({}, defaults, settings); | |
$element.slick(options); | |
// go to active slide on initialization | |
$element.slickGoTo(activeIndex); | |
}); | |
} | |
return { | |
restrict: 'A', | |
replace: true, | |
controller: 'SlickCarouselController', | |
controllerAs: 'slick', | |
scope: { | |
slides: '=', | |
interstitial: '=', | |
settings: '@', | |
active: '=', | |
onchange: '&', | |
count: '=', | |
interstitialLink: '=', | |
autoplay: '@', | |
responsive: '@', | |
state: '@' | |
}, | |
templateUrl: 'slick-carousel.tpl.html', | |
link: link | |
}; | |
} | |
angular | |
.module('slickCarousel', [ | |
'rt.debounce' | |
]) | |
.directive('slickCarousel', SlickCarouselDirective); | |
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
<div class="gallery-type-slider clearfix" ng-class="{'with-replay': slick.replayable}"> | |
<div class="slide" ng-class="'{{slide.type}}' == 'ad' ? 'slide-ad' : ''" data-ng-repeat="slide in slick.slides"> | |
<figure ng-if="slide.type != 'ad'"> | |
<img data-lazy="{{ slide.src }}"> | |
<figcaption> | |
<h3>{{ slide.title }}</h3> | |
<p data-ng-bind-html="slide.summary"></p> | |
</figcaption> | |
</figure> | |
</div> | |
<span class="autoplay-icon autoplay-button irg-play-gallery" ng-show="slick.autoplay"></span> | |
<span class="autoplay-icon play-button irg-play-button" ng-show="slick.autoplay"></span> | |
<span class="autoplay-icon pause-button irg-pause-button" ng-show="slick.autoplay"></span> | |
<span class="return" ng-show="slick.parent"> | |
<a href="{{ slick.parent.article_url }}" target="_self"> | |
<span class="caret"></span> Return to Article</a> | |
</span> | |
<span class="replay" ng-show="slick.replayable"> | |
<a ng-click="slick.replay()"> | |
<span class="irg-replay"></span> | |
</a> | |
</span> | |
<p class="interstitial" ng-show="slick.interstitial" ng-class="{'with-replay': slick.replayable}"> | |
<span class="interstitial-content"> | |
<a href="{{ slick.interstitial.link }}" target="_self" class="clearfix"> | |
<img data-ng-src="{{ slick.interstitial.img }}" alt=""> | |
<span class="interstitial-meta"> | |
<em class="slug">{{ slick.interstitial.subhead }}</em> | |
<strong class="title add-link-arrows">{{ slick.interstitial.title }}</strong> | |
</span> | |
<strong class="arrow"> | |
<span class="irg-interstitial-arrow"></span> | |
</strong> | |
</a> | |
</span> | |
</p> | |
<span class="interstitial-bg" ng-show="slick.interstitial"></span> | |
</div> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment