Custom Carousel for Ionic Framework using angular directives

By: Ryan Wong at

I couldn’t find a carousel plugin for ionic framework which can create a lightbox that lets you swipe through images. So I made my own.

Template HTML

1
<carousel images="images" show="show"></carousel>

Page Controller

1
2
3
4
5
6
7
8
9
10
11
function PageCtrl($s){
$s.show = false;
$s.images = [
'http://lorempixel.com/300/200/',
'http://lorempixel.com/300/200/food',
'http://lorempixel.com/300/200/nature',
'http://lorempixel.com/300/200/sports',
];
}
angular.module('controller',[])
.controller('PageCtrl', ['$scope', PageCtrl]);

To trigger carousel, you should bind a ng-click event somewhere to toggle show.

Directive HTML
I called it carousel.html .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="carousel-container" ng-show="show">
<div class="carousel-close" ng-click="close()"><i class="ion-close"></i></div>
<div class="carousel">

<ul class="carousel-list" on-swipe-right="nextSlide()" on-swipe-left="prevSlide()">
<li class="carousel-image" ng-repeat="oneImage in images" style="{{imageStyle()}}">
<img ng-src="{{oneImage}}">
</li>
</ul>

<div class="carousel-arrow-left" ng-click="prevSlide();"><i class="ion-arrow-left-a"></i></div>
<div class="carousel-arrow-right" ng-click="nextSlide();"><i class="ion-arrow-right-a"></i></div>
</div>
</div>

SCSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
.carousel-container{
width: 100%;
position: fixed;
background-color: rgba(0,0,0,0.4);
z-index: 100;
height: 100%;
overflow-x:hidden;
overflow-y:hidden;
.carousel-close{
position: absolute;
right: 5px;
top: 5px;
color: white;
}
.carousel{
margin-top:33%;
position:relative;
min-height: 400px;
overflow: hidden;
margin-left: auto;
margin-right: auto;

ul{
list-style: none;
list-style-type: none;
padding-left: 0px;
position: absolute;
li {
float:left;
position: relative;
img {
width: 90%;
max-width: 90%;
margin-left: auto;
margin-right: auto;
display: block;
}
}
}
.carousel-arrow-left{
width: 20px;
height: 20px;
position: absolute;
top: 25%;
left: 10%;
position: absolute;
z-index: 102;
opacity: 0.5;
i {
font-size: 32px;
color: white;
}
}
.carousel-arrow-right{
width: 20px;
height: 20px;
top: 25%;
right: 10%;
position: absolute;
z-index: 102;
opacity: 0.5;
i {
font-size: 32px;
color: white;
}
}
}
}

Directive Js code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
function carousel() {
return {
restrict: 'E',
templateUrl: 'directive/carousel.html',
scope: {
images: "=",
show: "="
},
link: function(scope) {
scope.index = 1;
scope.max = scope.images.length;
scope.firstTime = true;
scope.ratio = Math.round(100/scope.max);
scope.imageWidth = window.innerWidth;
var carouselObj = angular.element(document.querySelector('.carousel'));
var carouselListObj = angular.element(document.querySelector('.carousel-list'));
var carouselImageObj = angular.element(document.querySelector('.carousel-image'));
carouselObj.css('width', scope.imageWidth + 'px');
carouselListObj.css('width', (100 * scope.max) + '%');

scope.nextSlide = function(){
if (scope.index == scope.max){
scope.index = 1;
carouselListObj.css('transform', 'translateX(0%)');
} else {
carouselListObj.css('transform', 'translateX(-' + scope.index * scope.ratio + '%)');
scope.index++;
}
};
scope.prevSlide = function(){
if (scope.index == 1){
carouselListObj.css('transform', 'translateX(0%)');
scope.index = scope.max;
if (scope.firstTime){
scope.index--;
carouselListObj.css('transform', 'translateX(-' + scope.index * scope.ratio + '%)');
scope.firstTime = false;
}
} else {
scope.index--;
carouselListObj.css('transform', 'translateX(-' + scope.index * scope.ratio + '%)');
}
};
scope.imageStyle = function(){
return 'width:' + scope.imageWidth + 'px;';
};
scope.close = function() {
scope.show = false;
scope.index = 1;
scope.firstTime = true;
};
}
};
}
angular.module('directives', [])
.directive('carousel', [carousel]);

In my code, I pulled a database row which contains images in the html.
I then had to bind ng-clicks to each image in the code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$s.content = databaseRow.text;
var imageTag = new RegExp('<img .*alt="" />','g');
var srcTag = new RegExp('src=".*" alt','g');
$s.imageList = $s.content.match(imageTag);
if (!$s.imageList){
$s.imageList = [];
}
$s.imageList = $s.imageList.map(function(oneSrc){
$s.content = $s.content.replace(oneSrc, oneSrc.replace('alt=""',
' alt="" ng-click="openCarousel()"'));
var oneImage = oneSrc.match(srcTag);
return oneImage[0].replace("src=\"", "").replace("\" alt", "");
});
$s.carousel = function(){
$s.show = true;
}

When displaying content in the html, I used my dynamic directive to rebind the new images ng-click.

Code I added to carousel directive

1
2
3
4
5
6
7
8
9
10
11
scope.$watch('images', function(images) {
scope.images = images;
scope.index = 1;
scope.max = images.length;
scope.firstTime = true;
scope.ratio = Math.round(100/scope.max);
scope.imageWidth = window.innerWidth;
carouselObj.css('width', scope.imageWidth + 'px');
carouselListObj.css('width', (100 * scope.max) + '%');
scope.imageStyle();
});

dynamic directive

1
2
3
4
5
6
7
8
9
10
11
12
function dynamic ($c) {
return {
restrict: 'A',
replace: true,
link: function (scope, ele, attrs) {
scope.$watch(attrs.dynamic, function(html) {
ele.html(html);
$c(ele.contents())(scope);
});
}
};
}

Hope this will help you out.