AngularJS实现一个简单的Carousel

来源:互联网 发布:世界销售网络分布图 编辑:程序博客网 时间:2024/05/17 03:10

利用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代码说明):

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

各变量说明如下:

  1. images是待显示的图片集合
  2. currentSlide表示当前显示的图片在images中的索引
  3. forward表示图片显示顺序。为true表示按照images中的索引从小到大显示;为false表示按照索引从大到小显示
  4. 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实现步骤'进行对比

  1. toSlide[pon] = true; ===> $('#item3').addClass('next');
  2. var reflow = toSlide.$element[0].offsetWidth; ===> var reflow = $('#item3')[0].offsetWidth;
  3. $timeout(function(){ fromSlide[lor] = toSlide[lor] = true; }); ===> $('#item2 #item3').addClass('left');
  4. 平移完成后
    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;下一张图片会立即显示出来,而不是缓慢地从右侧平移过来, 关于这一点我暂时还没有想明白为什么,希望清楚的小伙伴告诉我一下,谢谢啦^-^!


0 0
原创粉丝点击