实现无缝Carousel

标签:
  • experience
  • angular

最近一段时间我在重构访名医官网,多了很多动画,设计也更精美。 其中有一个就是实现无缝Carousel, 结合angularjs ,我想应该是很简单的吧。之前也看过不少这方面的资料,但总是不理解,一直压在心里,成了心结了。正好,借这次机会,彻底了解下。

淘宝网首页,就有这方面的完美例子,打开开发工具,仔细观察了布局和变化后,我写下了结论。 “帧布局是5-1-2-3-4-5-1,猜测是在跳转到最后一帧的时候,取消了transition,同时快速定位到第一幁,再加上transition属性,这样肉眼就看不出来。感觉无限滚动一样。”,我问了群里的朋友,一位朋友用例子证实了我的猜测,这大大增强了我的信心。

以前的项目,我用过angular-carousel,不是无缝实现,而且用起来也感觉不好,多了不可控的因素,但实现源码还是值得一看的,很有启发。

所以决定自己实现一个,花了大约一天的时间,反复测试后,终于OK了。

Directive simpleCarousel 的实现

//todo 参考angular-carousel 实现动态width
app.directive('simpleCarousel', function(fmyApi) {
  return {
    restrict: 'A',
    scope : { slideIndex : "=" },
    link: function($scope, element, attrs) {
      //3-1-2-3-1 实现双向无缝滚动
      // detect supported CSS property
      var transformProperty = 'transform';
      var transitionProperty = "transition";

      var prefix = ['webkit', 'Moz', 'O', 'ms'];
      for (var i = 0; i < prefix.length; i++) {
        var e = prefix[i] + "Transform";
        if (typeof document.body.style[e] !== 'undefined') {
          transformProperty = e;
          transitionProperty = prefix[i] + "Transition";
          break;
        }
      };

      var transV = "transform .25s ease";
      if (fmyApi.isIe9 || fmyApi.isIe8) {
         transV = "margin-left .25s ease";
      }

      var childrens = element.children(), l = childrens.length;
      var l = element.children().length;
      var firstC = angular.element(childrens[0]), lastC = angular.element(childrens[l - 1]);
      var w = firstC[0].clientWidth;
      element.append(firstC.clone()).prepend(lastC.clone());

      $scope.$watch("slideIndex",function(nvalue,ovalue) {
        element[0].style[transitionProperty] = transV;
        if (fmyApi.isIe8 || fmyApi.isIe9) {
          element[0].style["marginLeft"] = "-" + (nvalue + 1) * w + "px";
        }else {
          element[0].style[transformProperty] = "translate3d(-" + ( nvalue + 1 ) * w + "px,0,0)" ;
        }

        if (nvalue == l || nvalue == -1) {
          element.on("transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd",function(e) {
            element[0].style[transitionProperty] = "none";
            if (nvalue == l) {
              if (fmyApi.isIe8 || fmyApi.isIe9) {
                element[0].style["marginLeft"] = "-" + w + "px";
              }else {
                element[0].style[transformProperty] = "translate3d(-" + w + "px,0,0)" ;
              }
            }else {
              if (fmyApi.isIe8 || fmyApi.isIe9) {
                element[0].style["marginLeft"] = "-" + l * w + "px";
              }else {
                element[0].style[transformProperty] = "translate3d(-" + l * w + "px,0,0)" ;
              }
            }
            element.off("transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd");
          });
        }

      });
    }
  };
});

页面布局

<div class='carousel-wrapper'>
        <ul class="carousel-cnt" simple-carousel slide-index='car1' ng-class='[car1Active,"c" + car1]'>
        ...
        </ul>
        <img class='prev' src="images/v2/left.png" alt="left" ng-click='direct("car1",-1)'>
        <img class='next' src="images/v2/right.png"  alt="right" ng-click='direct("car1",1)'>
      </div>

控制器

app.controller('indexCtrl', function($scope, $timeout, fmyApi) {
...
  $scope.car1 = 0;
  $scope.car2 = 0;

  $scope.direct = function(car, step) {
    var tt = $scope[car] + step;
    if ( tt == -2 ) {
      tt = 1;
    }
    if ( tt == 4 ) {
      tt = 1;
    }
    $scope[car] = tt;
  };
...
});
返回首页 06 August 2014