您的位置:首页 > Web前端 > AngularJS

AngularJS实现一个简单的Carousel

2016-03-18 13:36 471 查看
利用AngularJS实现了一个简单版的Bootstrap Carousel,即单击Next或Prev按钮时能翻看后一张或前一张图片。 在这里总结一下,有些地方我自己也存在疑惑,望各位不吝赐教。


Bootstrap Carousel实现步骤

1、这个方法很方便,不需要再额外编写js代码按照此方法即可实现此效果,看相关代码:



点滴积累:

通过 data 属性:使用 data 属性可以很容易控制轮播(Carousel)的位置。

属性 data-slide 接受关键字 prev 或 next,用来改变幻灯片相对于当前位置的位置。

使用 data-slide-to 来向轮播床底一个原始滑动索引,data-slide-to="2" 将把滑块移动到一个特定的索引,索引从 0 开始计数。

2、先看一下相关的HTML片段,div.item的id是为了方便说明添加的,与实现无关。
<div class="carousel">
<div class="carousel-inner">
<div class="item" id="item1">
<img src="img1" alt=""/>
</div>
<div class="item active" id="item2">
<img src="img2" alt=""/>
</div>
<div class="item" id="item3">
<img src="img3" alt=""/>
</div>
</div>
</div>


在上面的示例代码中,我们要轮流播放img1、img2和img3三张图片。当前显示的是img2(因为item2属于active类),下一张要显示的图片假设是img3。 具体步骤如下(利用jQuery代码说明):

$('#item3').addClass('next');(将#item3放在#item2的右侧,但是被浏览器隐藏起来)
var reflow = $('#item3')[0].offsetWidth;( 这一步非得做吗?我自己实验了一下,如果不重绘UI,好像效果也一样 )
$('#item2 #item3').addClass('left');(这会导致#item2和#item3同时向左平移, bootstrap的css文件中注册了item类元素针对translate属性的transition)
当平移结束时,$('#item2').removeClass('active left'); $('#item3').removeClass('next left').addClass('active');


AngularJS实现

先看index.html中的代码片段
<body>
<div ng-app="app">
<div class="row">
<div class="col-md-2">
<div ng-controller="CarouselController">
<carousel current-slide="currentSlide" forward="forward">
<slide ng-repeat="img in images">
<img ng-src="{{img}}" alt=""/>
</slide>
</carousel>
<button ng-click="prev()" class="btn btn-primary">Prev</button>
<button ng-click="next()" class="btn btn-primary">Next</button>
</div>
</div>
</div>
</div>

<script src="angular.js"></script>
<script src="ui-bootstrap.js"></script>
<script src="carousel.js"></script>
<script src="app.js"></script>
</body>


下面一个个地解释涉及到的directive:


CarouselController

angular.module('app', ['carousel']).
controller('CarouselController', function($scope, $element){
$scope.images = [
'./imgs/img1-large.jpg',
'./imgs/img2-large.jpg',
'./imgs/img3-large.jpg',
'./imgs/img4-large.jpg',
'./imgs/img5-large.jpg'
];
$scope.currentSlide = 1;

$scope.prev = function(){
$scope.forward = false;
$scope.currentSlide = ($scope.currentSlide + $scope.images.length - 1) % $scope.images.length;
};
$scope.next = function(){
$scope.forward = true;
$scope.currentSlide = ($scope.currentSlide + 1) % $scope.images.length;
};
});


各变量说明如下:

images是待显示的图片集合
currentSlide表示当前显示的图片在images中的索引
forward表示图片显示顺序。为true表示按照images中的索引从小到大显示;为false表示按照索引从大到小显示
prev()和next()是单击Prev和Next按钮时执行的代码,它们只是更新了currentSlide和forward


carousel directive

angular.module('carousel', ['ui.bootstrap.transition']).
directive('carousel', function($transition, $timeout){
return {
restrict: 'E',
replace: true,
transclude: true,
scope: {
forward: '=',
currentSlide: '='
},
controller: function($scope, $element) {
$scope.slides = [];
this.addSlide = function (slide) {
if($scope.currentSlide === $scope.slides.length){
slide.active = true;
}
$scope.slides.push(slide);
};
this.removeSlide = function(slide){
var idx = $scope.slides.indexOf(slide);
$scope.slides.splice(idx, 1);
};

var oldSlide = $scope.currentSlide;
var transition;
function go(from, to, forward){
if(from === to) return;

var lor = forward ? 'left' : 'right';
var pon = forward ? 'next' : 'prev';
var fromSlide = $scope.slides[from];
var toSlide = $scope.slides[to];

toSlide[pon] = true;
//var reflow = toSlide.$element.offsetWidth;

$timeout(function(){
fromSlide[lor] = toSlide[lor] = true;
});

transition = $transition(toSlide.$element, {}).
then(function(){
angular.extend(fromSlide, {active: false, left: false, right: false});
angular.extend(toSlide, {active: true, left: false, right: false, prev: false, next: false});

oldSlide = to;
transition = null;
});
}

$scope.$watch('currentSlide', function(newSlide){
if(newSlide === oldSlide) return;

var forward = $scope.forward;
if(!transition){
go(oldSlide, newSlide, forward);
}
});
},
templateUrl: 'kits/carousel/carousel.tmpl.html'
};
});


其$scope中有两个属性forward和currentSlide,它们是与CarouselController中的forward以及currentSlide绑定在一起的,而且通过$scope.$watch('currentSlide', ...)来监听currentSlide的变化。于是,当用户通过单击Prev或Next按钮来改变CarouselController中的currentSlide和forward时,carousel directive能及时地发现,并通过go(oldSlide, newSlide,
forward)来执行图片切换。


carousel directive的HTML模版

<div class="carousel slide">
<div class="carousel-inner" ng-transclude>
</div>
</div>


这没什么好说,只不过其中有个data-ng-transclude来指定子元素的位置


slide directive

angular.module('carousel', ['ui.bootstrap.transition']).
directive('slide', function(){
return {
scope: {},
restrict: 'E',
require: '^carousel',
replace: true,
transclude: true,
templateUrl: 'kits/carousel/slide.tmpl.html',
link: function(scope, element, attrs, carousel){
scope.$element = element;
carousel.addSlide(scope);

scope.$on('destroy', function(){ carousel.removeSlide(scope); })
}
}
});


通过require: '^carousel',link函数中能获取到carousel的controller。通过carousel controller 的addSlide(scope)方法,每个slide都会将自身的scope添加到carousel scope的slides数组中。为什么要这样做呢?carousel中定义的go函数在实现transition时需要改变当前slide element和下一个slide element的class属性(参见前面的“Bootstrap Carousel实现步骤”小节),而这些class属性是和slide
scope中相应的变量绑定在一起的。比如,当slide scope中active为true时,相应的slide element就会拥有active类。这是通过ng-class实现的,具体参见下面的slide directive模版。
<div class="item" data-ng-class="{'active': active, 'prev': prev, 'next': next, 'left': left, 'right': right}" ng-transclude>
</div>


carousel directive中go(oldSlide, newSlide, forward)的实现

为方便查看,代码再次贴到此处
function go(from, to, forward){
if(from === to) return;

var lor = forward ? 'left' : 'right';
var pon = forward ? 'next' : 'prev';
var fromSlide = $scope.slides[from];
var toSlide = $scope.slides[to];

toSlide[pon] = true;
var reflow = toSlide.$element[0].offsetWidth;

$timeout(function(){
fromSlide[lor] = toSlide[lor] = true;
});

transition = $transition(toSlide.$element, {}).
then(function(){
angular.extend(fromSlide, {active: false, left: false, right: false});
angular.extend(toSlide, {active: true, left: false, right: false, prev: false, next: false});

oldSlide = to;
transition = null;
});
}


假设forward === true,我将这里执行的步骤与'Bootstrap Carousel实现步骤'进行对比

toSlide[pon] = true; ===> $('#item3').addClass('next');
var reflow = toSlide.$element[0].offsetWidth; ===> var reflow = $('#item3')[0].offsetWidth;
$timeout(function(){ fromSlide[lor] = toSlide[lor] = true; }); ===> $('#item2 #item3').addClass('left');
平移完成后

angular.extend(fromSlide, {active: false, left: false, right: false});

angular.extend(toSlide, {active: true, left: false, right: false, prev: false, next: false});

===>

当平移结束时

$('#item2').removeClass('active left'); $('#item3').removeClass('next left').addClass('active');

可以看出没有本质区别。上面步骤3中,我用到了$timeout,这是我通过多次实验想到的。因为如果直接在$timeout外面写fromSlide[lor] = toSlide[lor] = true;下一张图片会立即显示出来,而不是缓慢地从右侧平移过来, 关于这一点我暂时还没有想明白为什么,希望清楚的小伙伴告诉我一下,谢谢啦^-^!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: