2 * Name: angular-carousel-3d
3 * GIT Page: https://github.com/Wlada/angular-carousel-3d
4 * Version: 0.1.1 - 2016-05-30T20:01:50.496Z
13 .module('angular-carousel-3d', [
23 .module('angular-carousel-3d')
24 .directive('carousel3dSlide', carousel3dSlide);
28 carousel3dSlide.$inject = [];
30 // == HTML rendering directive
31 function carousel3dSlide() {
32 var carousel3dSlide = {
33 require: '^carousel3d',
35 template: '<div class=\"slide-3d\" ng-click=\"carousel3d.slideClicked($index)\" ng-dblclick=\"carousel3d.slideDblClicked($index)\" ng-swipe-left=\"carousel3d.goPrev()\" ng-swipe-right=\"carousel3d.goNext()\" ng-transclude></div>',
43 // ========================================
44 function linkFunc(scope, element, attrs, ctrl, transclude) {
45 scope.carousel3d = ctrl;
48 return carousel3dSlide;
55 .module('angular-carousel-3d')
56 .controller('Carousel3dController', Carousel3dController);
59 // == Directive Controller
60 // ========================================
61 Carousel3dController.$inject = ['$scope', '$element', '$attrs', '$timeout', '$interval', '$log', '$window', 'Carousel3dService'];
63 function Carousel3dController($scope, $element, $attrs, $timeout, $interval, $log, $window, Carousel3dService) {
67 vm.isSuccessful = false;
68 vm.isRendered = false;
70 vm.autoRotation = null;
72 vm.slideClicked = slideClicked;
73 vm.slideDblClicked = slideDblClicked;
81 // == Watch changes on model and options object
82 $scope.$watch('[vm.model, vm.options]', init, true);
86 .build(vm.model, vm.options)
88 function handleResolve(carousel) {
90 carousel3d = carousel;
92 vm.slides = carousel3d.slides;
93 vm.controls = carousel3d.controls;
95 vm.isSuccessful = true;
97 var outerHeight = carousel3d.getOuterHeight(),
98 outerWidth = carousel3d.getOuterWidth();
100 $element.css({'height': outerHeight + 'px'});
102 $timeout(function () {
104 $wrapper = angular.element($element[0].querySelector('.carousel-3d'));
105 $wrapper.css({'width': outerWidth + 'px', 'height': outerHeight + 'px'});
106 $slides = $wrapper.children();
112 // == Preloaded images reject handler
113 function handleReject(carousel) {
115 $element.css({'height': carousel.getOuterHeight() + 'px'});
117 vm.isLoading = false;
118 vm.isSuccessful = false;
120 // == Preloaded images notify handler which is executed multiple times during preload
121 function handleNotify(event) {
122 vm.percentLoaded = event.percent;
128 function render(animate, speedTime) {
129 carousel3d.setSlides();
131 var outerHeight = carousel3d.getOuterHeight(),
132 outerWidth = carousel3d.getOuterWidth(),
133 slideTop = (carousel3d.topSpace === "auto") ? 0 : ((outerHeight / 2) - (outerHeight / 2)),
134 slideLeft = ((carousel3d.width / 2) - (outerWidth / 2)),
135 speed = (speedTime) ? (speedTime / 1000) : (carousel3d.animationSpeed / 1000),
138 // == Set other slides styles
139 angular.forEach(carousel3d.slides, function (slide, index) {
141 position: 'absolute',
143 visibility: 'hidden',
145 top: slideTop + 'px',
146 'border-width': carousel3d.border + 'px',
152 angular.extend(css, {
153 '-webkit-transition': "all " + speed + "s ",
154 '-moz-transition': "all " + speed + "s ",
155 '-o-transition': "all " + speed + "s ",
156 '-ms-transition': "all " + speed + "s ",
157 'transition': "all " + speed + "s "
161 getSlide(index).css(css);
164 // == Set first slide styles
165 getSlide(carousel3d.currentIndex)
170 visibility: 'visible',
171 '-webkit-transform': 'none',
172 '-moz-transform': 'none',
173 '-o-transform': 'none',
174 '-ms-transform': 'none',
176 left: slideLeft + 'px',
177 top: slideTop + 'px',
178 width: outerWidth + "px",
179 height: outerHeight + "px"
182 angular.forEach(carousel3d.rightSlides, function (slide, index) {
183 var css = setCss(index, zIndex, true);
191 visibility: 'visible',
196 angular.forEach(carousel3d.leftSlides, function (slide, index) {
197 var css = setCss(index, zIndex);
205 visibility: 'visible',
210 if (carousel3d.total > carousel3d.visible) {
212 var rCSS = setCss(carousel3d.rightSlides.length - 1, carousel3d.rightSlides.length - 1, true),
213 lCSS = setCss(carousel3d.leftSlides.length - 1, carousel3d.leftSlides.length - 1);
215 getSlide(carousel3d.rightOutSlide).css(rCSS);
216 getSlide(carousel3d.leftOutSlide).css(lCSS);
219 if(carousel3d.autoRotationSpeed > 0) {
220 vm.autoRotation = $interval(function() {
221 if(vm.dir === 'rtl') {
226 }, carousel3d.autoRotationSpeed);
228 vm.isRendered = true;
231 function setCss(i, zIndex, positive) {
233 var leftRemain = (carousel3d.space == "auto") ? parseInt((i + 1) * (carousel3d.width / 1.5)) : parseInt((i + 1) * (carousel3d.space)),
234 transform = (positive) ?
235 'translateX(' + (leftRemain) + 'px) translateZ(-' + (carousel3d.inverseScaling + ((i + 1) * 150)) + 'px) rotateY(-' + carousel3d.perspective + 'deg)' :
236 'translateX(-' + (leftRemain) + 'px) translateZ(-' + (carousel3d.inverseScaling + ((i + 1) * 150)) + 'px) rotateY(' + carousel3d.perspective + 'deg)',
238 top = (carousel3d.topSpace === "auto") ? "none" : parseInt((i + 1) * (carousel3d.space)),
244 '-webkit-transform': transform,
245 '-moz-transform': transform,
246 '-o-transform': transform,
247 '-ms-transform': transform,
248 'transform': transform,
258 function goSlide(index, motionless, farchange) {
260 if (angular.isFunction(vm.onBeforeChange)) {
262 index: carousel3d.currentIndex
266 carousel3d.setCurrentIndex((index < 0 || index > carousel3d.total - 1) ? 0 : index);
268 if (carousel3d.isLastSlide()) {
270 if (angular.isFunction(vm.onLastSlide)) {
272 index: carousel3d.currentIndex
277 angular.forEach($slides, function (slide, index) {
278 angular.element($slides[index]).removeClass('current');
281 carousel3d.setLock(true);
283 render(true, carousel3d.animationSpeed);
285 $timeout(function () {
287 }, carousel3d.animationSpeed);
292 function goNext(farchange) {
294 farchange = (farchange) ? farchange : false;
296 if ((!farchange && carousel3d.getLock()) || (!carousel3d.loop && carousel3d.isLastSlide())) {
300 if (carousel3d.isLastSlide()) {
301 goSlide(0, false, farchange);
304 goSlide(carousel3d.currentIndex + 1, false, farchange);
310 function goPrev(farchange) {
312 farchange = (farchange) ? farchange : false;
314 if ((!farchange && carousel3d.getLock()) || (!carousel3d.loop && carousel3d.isFirstSlide())) {
318 if (carousel3d.isFirstSlide()) {
319 goSlide(carousel3d.total - 1, false, farchange);
322 goSlide(carousel3d.currentIndex - 1, false, farchange);
328 function goFar(index) {
329 var diff = (index === carousel3d.total - 1 && carousel3d.isFirstSlide()) ? -1 : (index - carousel3d.currentIndex);
331 if (carousel3d.isLastSlide() && index === 0) {
335 var diff2 = (diff < 0) ? -diff : diff,
338 for (var i = 0; i < diff2; i++) {
339 var timeout = (diff2 === 1) ? 0 : (timeBuff);
341 $timeout(function () {
342 (diff < 0) ? goPrev(diff2) : goNext(diff2);
345 timeBuff += (carousel3d.animationSpeed / (diff2));
349 function animationEnd() {
350 carousel3d.setLock(false);
352 if (vm.onSlideChange) {
354 index: carousel3d.currentIndex
359 function getSlide(index) {
360 return (index >= 0) ? angular.element($slides[index]) : angular.element($slides[carousel3d.total + index]);
363 function slideClicked(index) {
364 $interval.cancel(vm.autoRotation);
366 if (carousel3d.currentIndex != index) {
368 if (!carousel3d.clicking) {
375 if (vm.onSelectedClick) {
377 index: carousel3d.currentIndex
383 function slideDblClicked(index) {
384 $interval.cancel(vm.autoRotation);
386 if (carousel3d.currentIndex == index) {
387 if (vm.onSelectedDblClick) {
388 vm.onSelectedDblClick({
389 index: carousel3d.currentIndex
401 .module('angular-carousel-3d')
402 .directive('carousel3d', carousel3d);
407 carousel3d.$inject = ['$timeout'];
409 function carousel3d($timeout) {
414 '<div class=\"carousel-3d-container\" ng-switch="vm.isLoading">' +
415 ' <div class="carousel-3d-loader" ng-switch-when=\"true\">' +
416 ' <div class=\"carousel-3d-loader-circle\" style=\"-webkit-transform:scale(0.75)\"><div><div></div><div></div></div></div>' +
417 ' <div class="carousel-3d-loader-percentage">{{ vm.percentLoaded }}</div>' +
419 ' <div ng-switch-when="false" ng-switch="vm.isSuccessful">' +
420 ' <div class=\"carousel-3d\" ng-switch-when=\"true\" ng-show="vm.isRendered" ng-transclude>' +
422 ' <p ng-switch-when=\"false\" class="carousel-3d-loader-error">There was a problem during load</p>' +
423 ' <div ng-if="vm.controls" class="carousel-3d-controls">' +
424 ' <div class="carousel-3d-next" ng-click=\"vm.goPrev()\"><i class="fa fa-chevron-left"></i></div>' +
425 ' <div class="carousel-3d-prev" ng-click=\"vm.goNext()\"><i class="fa fa-chevron-right"></i></div>' +
433 onSelectedClick: '&',
434 onSelectedDblClick: '&',
439 controller: 'Carousel3dController as vm',
440 bindToController: true,
442 compile: compileFunc,
447 // == Directive Compile
448 // =======================================
449 //compileFunc.$inject = ['element', 'attributes', '$attrs'];
451 function compileFunc(element, attributes) {
458 // ========================================
460 function linkFunc(scope, element, attrs, ctrl, transclude) {
471 .module('angular-carousel-3d')
472 .factory("Carousel3dService", Carousel3dService);
474 Carousel3dService.$inject = ['$rootScope', '$q', '$log'];
476 function Carousel3dService($rootScope, $q, $log) {
478 function Carousel3d(slides, params) {
479 this.slides = slides || [];
480 this.leftSlides = [];
481 this.rightSlides = [];
482 this.leftOutSlide = '';
483 this.rightOutSlide = '';
486 this.loop = params.loop || false;
487 this.clicking = params.clicking || false;
494 this.total = this.slides.length;
495 this.currentIndex = 0;
497 this.sourceProp = params.sourceProp;
498 this.visible = params.visible || 5;
499 this.perspective = params.perspective || 35;
500 this.animationSpeed = params.animationSpeed || 500;
501 this.dir = params.dir || 'ltr';
502 this.width = params.width || 360;
503 this.height = params.height || 270;
504 this.border = params.border || 0;
505 this.space = params.space || 'auto';
506 this.topSpace = params.topSpace || 'auto';
507 this.controls = params.controls || false;
508 this.startSlide = params.startSlide || 0;
509 this.inverseScaling = params.inverseScaling || 300;
510 this.autoRotationSpeed = params.autoRotationSpeed || 0;
511 this.state = this.states.PENDING;
512 this.deferred = $q.defer();
513 this.promise = this.deferred.promise;
517 // ========================================
519 Carousel3d.build = function (model, params) {
520 var carousel = new Carousel3d(model, params || {});
522 return carousel.load().promise.then(function () {
523 carousel.visible = (carousel.visible > carousel.total) ? carousel.total : carousel.visible;
525 carousel.currentIndex = carousel.startSlide > carousel.total - 1 ? carousel.total - 1 : carousel.startSlide;
528 if (carousel.visible !== 2) {
529 carousel.visible = (carousel.visible % 2) ? carousel.visible : carousel.visible - 1;
541 // == Private methods
542 // ========================================
545 constructor: Carousel3d,
546 isInitiated: isInitiated,
547 isRejected: isRejected,
548 isResolved: isResolved,
550 handleImageError: handleImageError,
551 handleImageLoad: handleImageLoad,
552 loadImageLocation: loadImageLocation,
553 getTotalNumber: getTotalNumber,
554 setStartSlide: setStartSlide,
555 getSlides: getSlides,
556 setSlides: setSlides,
557 setCurrentIndex: setCurrentIndex,
558 getOuterWidth: getOuterWidth,
559 getOuterHeight: getOuterHeight,
562 isLastSlide: isLastSlide,
563 isFirstSlide: isFirstSlide,
564 getSourceProp: getSourceProp
567 function isInitiated() {
568 return ( this.state !== this.states.PENDING );
571 function isRejected() {
572 return ( this.state === this.states.REJECTED );
575 function isResolved() {
576 return ( this.state === this.states.RESOLVED );
581 if (this.isInitiated()) {
586 this.state = this.states.LOADING;
588 if (!this.sourceProp) {
589 this.deferred.resolve(this);
592 for (var i = 0; i < this.total; i++) {
593 this.loadImageLocation(this.slides[i]);
601 function handleImageError(imageLocation) {
604 if (this.isRejected()) {
608 this.state = this.states.REJECTED;
609 this.deferred.reject(this);
612 function handleImageLoad(imageLocation) {
615 if (this.isRejected()) {
619 this.deferred.notify({
620 percent: Math.ceil(this.loadCount / this.total * 100),
621 imageLocation: imageLocation
624 if (this.loadCount === this.total) {
625 this.state = this.states.RESOLVED;
628 this.deferred.resolve(this);
632 function loadImageLocation(imageLocation) {
637 image.onload = function (event) {
638 $rootScope.$apply(function () {
639 carousel.handleImageLoad(event.target.src);
640 image = event = null;
645 image.onerror = function (event) {
646 $rootScope.$apply(function () {
647 carousel.handleImageError(event.target.src);
648 image = event = null;
652 image.src = imageLocation[this.sourceProp];
655 function getTotalNumber() {
659 function setStartSlide(index) {
660 this.startSlide = (index < 0 || index > this.total) ? 0 : index;
663 function setCurrentIndex(index) {
664 return this.currentIndex = index;
667 function getOuterWidth() {
668 return parseInt(this.width + this.border);
671 function getOuterHeight() {
672 return parseInt(this.height + this.border, 10);
675 function setLock(value) {
676 return this.lock = value;
683 function getSlides() {
687 function setSlides() {
688 var num = Math.floor(this.visible / 2) + 1,
691 this.leftSlides = [];
692 this.rightSlides = [];
694 for (var m = 1; m < num; m++) {
695 var eq1 = (this.dir === dir) ? (this.currentIndex + m) % (this.total) : (this.currentIndex - m) % (this.total),
696 eq2 = (this.dir === dir) ? (this.currentIndex - m) % (this.total) : (this.currentIndex + m) % (this.total);
698 this.leftSlides.push(eq1);
699 this.rightSlides.push(eq2);
702 var rightOut = this.leftOutSlide = (this.currentIndex - num),
703 leftOut = this.rightOutSlide = ((this.total - this.currentIndex - num) <= 0) ? (-parseInt(this.total - this.currentIndex - num)) : (this.currentIndex + num);
705 if (this.dir === dir) {
706 this.leftOutSlide = rightOut;
707 this.rightOutSlide = leftOut;
713 function isLastSlide() {
714 return this.currentIndex === this.total - 1;
717 function isFirstSlide() {
718 return this.currentIndex === 0;
721 function getSourceProp() {
722 return this.sourceProp;
725 angular.extend(Carousel3d.prototype, proto);
727 return ( Carousel3d );