利用AngularJS实现了一个简单版的Bootstrap Carousel,即单击Next或Prev按钮时能翻看后一张或前一张图片。 在这里总结一下,有些地方我自己也存在疑惑,望各位不吝赐教。
1、这个方法很方便,不需要再额外编写js代码按照此方法即可实现此效果,看相关代码:
点滴积累:
通过 data 属性:使用 data 属性可以很容易控制轮播(Carousel)的位置。
属性 data-slide 接受关键字 prev 或 next,用来改变幻灯片相对于当前位置的位置。
使用 data-slide-to 来向轮播床底一个原始滑动索引,data-slide-to="2" 将把滑块移动到一个特定的索引,索引从 0 开始计数。
2、先看一下相关的HTML片段,div.item的id是为了方便说明添加的,与实现无关。
class="carousel">
class="carousel-inner">
class="item" id="item1">
src="img1" alt=""/>
class="item active" id="item2">
src="img2" alt=""/>
class="item" id="item3">
src="img3" alt=""/>
在上面的示例代码中,我们要轮流播放img1、img2和img3三张图片。当前显示的是img2(因为item2属于active类),下一张要显示的图片假设是img3。 具体步骤如下(利用jQuery代码说明):
先看index.html中的代码片段
ng-app="app">
class="row">
class="col-md-2">
ng-controller="CarouselController">
current-slide="currentSlide" forward="forward">
ng-repeat="img in images">
ng-src="{{img}}" alt=""/>
下面一个个地解释涉及到的directive:
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;
};
});
各变量说明如下:
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)来执行图片切换。
class="carousel slide">
class="carousel-inner" ng-transclude>
这没什么好说,只不过其中有个data-ng-transclude来指定子元素的位置
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模版。
class="item" data-ng-class="{'active': active, 'prev': prev, 'next': next, 'left': left, 'right': right}" ng-transclude>
为方便查看,代码再次贴到此处
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实现步骤'进行对比
可以看出没有本质区别。上面步骤3中,我用到了$timeout,这是我通过多次实验想到的。因为如果直接在$timeout外面写fromSlide[lor] = toSlide[lor] = true;下一张图片会立即显示出来,而不是缓慢地从右侧平移过来, 关于这一点我暂时还没有想明白为什么,希望清楚的小伙伴告诉我一下,谢谢啦^-^!