【问题标题】:fading content in a carousel with angular带有角度的旋转木马中的褪色内容
【发布时间】:2016-01-31 12:09:12
【问题描述】:

我是 Angular 的新手,我正在努力解决这个问题,尽管我现在被困在基于内容的轮播中的动画内容上。我自己构建了这个轮播,但我事先没有考虑过“幻灯片”之间的褪色内容会如何影响它。

目前我的轮播的结构是:

<article class="container-fluid carousel-container" ng-controller="carouselController">
    <div class="container">
        <div class="prev-container">
            <button class="prev" ng-click="prevSlide()"><span>&lsaquo;</span></button>
        </div>
        <div class="slide">
            <div class="slide-left">
                <h1 class="slide-inner" ng-cloak>{{panels[slide].title}}</h1>
                <p class="slide-inner" ng-cloak>{{panels[slide].body}}</p>
            </div>
        </div>
        <div class="next-container">
            <button class="next" ng-click="nextSlide()"><span>&rsaquo;</span></button>
        </div>
    </div>
</article>

我只是更改单个“幻灯片”的内容,而不是实际的幻灯片。稍后我将在slide-right 容器中添加图像。

此功能位于carouselController

app.controller('carouselController', function($scope){
    $scope.slide = 0;
    $scope.prevSlide = function(){
        if ($scope.slide <= 0) {
            $scope.slide = ($scope.panels.length - 1);
        } else {
            $scope.slide--;
        };
    };
    $scope.nextSlide = function(){
        if ($scope.slide >= $scope.panels.length - 1) {
            $scope.slide = 0;
        } else {
            $scope.slide++;
        };
    };
    $scope.panels = [
        {
            id: 1,
            title: "1 - Lorem ipsum",
            body: "1 - Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatibus, dolores voluptas quod fuga id nihil in! Dolore accusantium perferendis deleniti voluptate, libero quis at molestias."

        },
        {
            id: 2,
            title: "2 - Lorem ipsum",
            body: "2 - Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatibus, dolores voluptas quod fuga id nihil in! Dolore accusantium perferendis deleniti voluptate, libero quis at molestias."
        },
        {
            id: 3,
            title: "3 - Lorem ipsum",
            body: "3 - Lorem ipsum dolor sit amet, consectetur adipisicing elit. Voluptatibus, dolores voluptas quod fuga id nihil in! Dolore accusantium perferendis deleniti voluptate, libero quis at molestias."
        }
    ];
});

这里的基本思想是从panels 数组中填充内容,单击下一个按钮或上一个按钮将分别增加或减少数组索引。然后填充slide-left div 中的标题和正文内容。我遇到的问题是当使用下一个或上一个按钮时,我想淡出那里的内容,并淡入新内容。我尝试了addClassremoveClass 以及许多其他方法,但都没有结果。最大的障碍是我实际上并没有更改幻灯片而只是更改内容,因为大多数资源和教程都认为我隐藏了一张幻灯片,并显示了下一张。我期待您的建议并感谢您的帮助,并随时批评我现有的代码。

【问题讨论】:

  • 考虑使用 ng-repeat 渲染所有幻灯片的面板,使用 ng-show 显示活动幻灯片。那么你的动画应该是可能的。
  • @ShaunScovil 这种方式不可能吗?我考虑过这一点,但更愿意解决问题而不是解决问题。
  • 我不会称其为解决方法。问题是,您是否期望某种交叉淡入淡出的动画,或者您只是想将幻灯片移出,然后更改其内容并将其移回?
  • 这个幻灯片示例可能会比我更好地回答您的问题:docs.angularjs.org/api/ngAnimate#javascript-based-animations
  • 我只想淡出内容并淡入新内容。理想情况下,我将能够做任何 css3 动画、过渡等。我看着 ng-animate 但问题是它取决于我所看到的临时类,并且由于这没有使用 ng-repeat 它没有临时类,有没有办法创建它们以及围绕它的行为?

标签: angularjs


【解决方案1】:

如果您想构建一个带有淡入/淡出过渡的轮播并避免使用ngAnimate,这是一种涉及创建一组自定义指令的方法。

工作示例:JSFiddle

模板

以下是使用此方法的 HTML 外观:

<div ng-controller="MyController as vm">
  <carousel panels="vm.panels">
    <carousel-panel>
      <h1>{{$title}}</h1>
      <p>{{$body}}</p>
    </carousel-panel>
    <button carousel-prev>
      &lsaquo; Prev
    </button>
    <button carousel-next>
      Next &rsaquo;
    </button>
  </carousel>
</div>

请注意,使用了四 (4) 个指令:

  1. 轮播
  2. 轮播面板
  3. carousel-next
  4. 轮播上一页

第一个 carousel 是一个包装器,它必须是其他元素的父元素。这使得每个子组件都可以与carousel 的控制器进行交互,同时清楚地分离每个子组件的关注点并且不会创建任何隐藏的依赖项。

轮播

这个指令有一个控制器,它向需要它的子指令公开数据和功能。它有一个panels 属性,它是来自其panels 属性的双向绑定。它还有一个slide 属性来跟踪哪个面板是可见的;以及用于调整该值的 next()prev() 函数。

angular.module('myApp.carousel')
  .controller('CarouselController', CarouselController)
  .directive('carousel', carouselDirective)
;

function CarouselController() {
  var carousel = this;
  carousel.slide = 0;
  carousel.current = function() {
    if (carousel.panels && carousel.panels.length) {
      return carousel.panels[carousel.slide];
    }
  };
  carousel.next = function() {
    if (carousel.panels && carousel.panels.length) {
      if (carousel.slide >= carousel.panels.length - 1) {
        carousel.slide = 0;
      } else {
        carousel.slide++;
      }
    }
  };
  carousel.prev = function() {
    if (carousel.panels && carousel.panels.length) {
      if (carousel.slide <= 0) {
        carousel.slide = carousel.panels.length - 1;
      } else {
        carousel.slide--;
      }
    }
  };
}

function carouselDirective() {
  return {
    bindToController: {
      panels: '='
    },
    controller: 'CarouselController',
    controllerAs: 'carousel',
    scope: true,
    template: '<ng-transclude></ng-transclude>',
    transclude: true
  };
}

轮播面板

该指令监视其父 CarouselControllerslide 属性,并在该值更改时为其自身添加一个 .fade 类。然后它设置一个超时时间(在本例中硬编码为 500 毫秒)并在延迟过去后删除 .fade 类。这是为使用纯 CSS 过渡而设计的,如下所述。

angular.module('myApp.carousel')
  .directive('carouselPanel', carouselPanelDirective)
;

function carouselPanelDirective($timeout) {
  return {
    link: postLink,
    require: '^carousel',
    template: '<ng-transclude></ng-transclude>',
    transclude: true
  };

  function postLink(scope, iElement, iAttrs, carousel) {
    scope.$watch(currentSlide, transition);

    function currentSlide() {
      return carousel.slide;
    }

    function fadeOut() {
      iElement.addClass('fade');
    }

    function fadeIn() {
      iElement.removeClass('fade');      
    }

    function refresh() {
      var current = carousel.current();
      scope.$title = current.title;
      scope.$body = current.body;
    }

    function transition(currentSlide, previousSlide) {
      if (currentSlide === previousSlide) {
        refresh();
      } else {
        fadeOut();
        $timeout(function() {
          refresh();
          fadeIn();
        }, 500);
      }
    }
  }
}

轮播下一个和上一个

这两个指令可以作为属性添加到按钮或任何其他元素。他们将“点击”事件处理程序添加到分别调用父级CarouselControllernext()prev() 函数的元素。

angular.module('myApp.carousel')
  .directive('carouselNext', carouselNextDirective)
  .directive('carouselPrev', carouselPrevDirective)
;

function carouselNextDirective() {
  return {
    link: postLink,
    require: '^carousel'
  };

  function postLink(scope, iElement, iAttrs, carousel) {
    iElement.on('click', onClick);
    scope.$on('$destroy', offClick);

    function onClick() {
      carousel.next();
      scope.$apply();
    }

    function offClick() {
      iElement.off('click', onClick);
    }
  }
}

function carouselPrevDirective() {
  return {
    link: postLink,
    require: '^carousel'
  };

  function postLink(scope, iElement, iAttrs, carousel) {
    iElement.on('click', onClick);
    scope.$on('$destroy', offClick);

    function onClick() {
      carousel.prev();
      scope.$apply();
    }

    function offClick() {
      iElement.off('click', onClick);
    }
  }
}

CSS

这些 CSS 规则以 carousel-panel 的嵌入 HTML 内容为目标,并使用 opacity 应用 CSS 过渡。请注意 0.5 秒(或 500 毫秒)的转换时间,这与我们面板的 $timeout 函数中的延迟相对应。

carousel-panel > ng-transclude > * {
  -webkit-transition: opacity 0.5s ease-in-out;
  -moz-transition: opacity 0.5s ease-in-out;
  -ms-transition: opacity 0.5s ease-in-out;
  -o-transition: opacity 0.5s ease-in-out;
  opacity: 1;
}

carousel-panel.fade > ng-transclude > * {
  opacity: 0;
}

结论

这似乎需要做很多工作,但它比仅仅将逻辑构建到单个控制器中更具可重用性、可扩展性和可维护性。通过使用transclusion,您可以启用应用程序模板来指示布局(与指令模板相对)。

【讨论】:

  • 感谢您的回复。这是我认为我需要花时间理解的事情,我会在完成后接受或评论。谢谢!
  • 没问题。关键部分是我添加 .fade 类的地方,然后设置一个超时来删除它。尽管刷新功能也起着关键作用,但您基本上可以使用现有代码执行此操作。您需要添加 .fade 类;等待过渡完成;然后更改模板中显示的标题和正文值,并删除 .fade 类。
  • @NathanLykke 这是一个使用您现有代码的示例:jsfiddle.net/sscovil/j8ksg9ur -- 这使用$element 在控制器中执行 DOM 操作,这违反了 Angular 最佳实践,但希望它能帮助您在您现有的代码和我建议的更改之间进行比较。
  • 谢谢,js fiddle 中的功能正是我想要做的,虽然我绝对想学习最佳实践,所以我会按照你的建议工作,如果我会告诉你它可以解决,或者如果我有并发症。非常感谢您对此提供的帮助。
  • 这绝对有效,但是我意识到我了解了其中的 10%,我需要做很多研究才能完全理解它。谢谢!