29 votes

Ne pas ng-show de l'élément jusqu'à ce que ng-hide CSS transition est-elle complète?

Simple question, mais je vais avoir de la mise en œuvre des ennuis. Si j'ai la suite de DOM de l'installation:

<h1 class="fade" ng-repeat="child in parent.children" ng-show="parent.activeChild== child ">@{{ child.title }}</h1>

Lorsque l' activeChild de la propriété de l' parent des changements de modèle, comment puis-je atténuer la actuellement active de l'enfant, avant les changements de modèle, puis fondu dans le nouveau active de l'enfant après changement.

- Je avoir de travail à peu près, avec juste les transitions CSS en utilisant ceci:

.fade.ng-hide-add {
    transition:opacity 1s ease;
}

.fade.ng-hide-remove {
    transition:opacity 1s ease 1s;
}

.fade.ng-hide-add {
    opacity:1;

    &.ng-hide-add-active {
        opacity:0;
    }
}

.fade.ng-hide-remove {
    opacity:0;

    &.ng-hide-remove-active {
        opacity:1;
    }
}

Mais, cela finit par produire ce problème (Plunkr):

Gif

Essentiellement, je veux de la chaîne de mon animation. J'ai essayé la lecture de la ng-animer des docs, mais je vais avoir de la difficulté à la syntaxe nécessaire pour livrer l'effet que je veux.

J'ai vu l'angle de docs avoir quelque chose comme ceci:

app.animation('.fade', [function() {
    return {
        addClass: function(element, className, doneFn) {
        },
        removeClass: function(element, className, doneFn) {
        }
    };
}]);
  • Qu'est - className? C'est la classe que je veux appliquer tout en diffusant? La classe que j'attends?
  • Qu'est - doneFn destinés à être? Je suppose que c'est une fonction qui est exécutée une fois l'animation terminée? Ce qui se passe là-dedans?
  • Que dois-je faire dans l' addClass et removeClass fonction alors, si j'ai déjà un doneFn?

L'Objectif

Je voudrais générer un travail d'animation directement à l'aide Angulaire du ngAnimate module, soit avec CSS ou JS. Comment puis-je y parvenir?

11voto

Abhilash P A Points 1442

Pourquoi utilisez-vous un distinct <h1> pour chaque rubrique. Vous pouvez utiliser un seul <h1> balise pour montrer votre tête.

J'ai créé une démo de votre problème et j'ai réussi à faire de votre exigence.

Mise à jour

Remarque, les codes sont modifiés pour utiliser ngAnimate module. Lorsque vous utilisez ngAnimate module, il va créer une classe .ng-hide lorsque vous cacher un élément,

Ici, c'est le contrôleur de votre application,

app2.controller("testController", ["$scope", "$timeout", function ($scope, $timeout) {

    $scope.heading = {};
    $scope.heading.show = true;

    $scope.parent = {};
    $scope.parent.children = ["A", "B", "C", "D"];
    $scope.parent.activeChild = "A";

    $scope.changeHeading = function (child) {
        $timeout(function () {
            $scope.parent.activeChild = child;
            $scope.heading.show = true;
        }, 1000);

    }
}]);

Et votre page html doit être ressembler à ceci,

<div ng-controller="testController">
    <h1 class="myAnimateClass" ng-show="heading.show" ng-class="{fadeIn : heading.fadeInModel==true, fadeOut : heading.fadeOutModel}"> {{parent.activeChild}} </h1>
    <p ng-repeat="child in parent.children" ng-click="heading.show = false;changeHeading(child)">{{child}}</p>
</div>

Et je l'ai utilisé CSS3 pour mettre en œuvre le fade in et le fade out de l'animation,

.myAnimateClass {
    -webkit-transition: opacity 1s ease-in-out;
    -moz-transition: opacity 1s ease-in-out;
    -o-transition: opacity 1s ease-in-out;
    -ms-transition: opacity 1s ease-in-out;
    transition: opacity 1s ease-in-out;
    opacity:1;
}

.myAnimateClass.ng-hide {
    opacity: 0;
}

Explication

Pour atteindre votre condition, je l'ai utilisé, ng-class et $timeout en angularJS.

Vous pouvez le voir, je n'ai qu'un <h1> balise pour afficher votre position. Lorsque je modifie le titre que je viens de changer la propriété de liaison $scope.parent.activeChild.

Et j'ai utilisé deux de la portée des variables $scope.heading.fadeOutModel et $scope.heading.fadeInModel d'ajouter et de supprimer des classes fadeIn et fadeOut dynamiquement.

Lorsque l'utilisateur clique pour changer le titre, j'ai ajouté de la classe fadeOut de votre tête. Ainsi, cela permettra de montrer une animation de fondu de sortie. Et aussi, j'ai tiré une fonction dans app.js, changeHeading().

Vous pouvez voir que, j'ai forcé l'angle d'attendre pour 1000 millisecondes à la fin du fondu sortant de l'animation. Après ce temps, il sera remplacer l'en-tête sélectionné à nouveau et ajouter une classe fadeIn. Donc, il va commencer l'animation de fondu.

Espérons que cela vous aidera !!!

4voto

jjbskir Points 1138

Plus de ng-animer de façon à afficher un élément spécifique en fonction d'une sélection serait d'utiliser ngSwitch. Cette directive est utilisée pour conditionnellement swap DOM structure de votre modèle basé sur une portée de l'expression. Ici est un exemple.

HTML

<button ng-repeat="item in items" ng-click="parent.selection = item">{{ item }}</button>
<div class="animate-switch-container" ng-switch on="parent.selection">
  <div class="animate-switch" ng-switch-when="foo">foo</div>
  <div class="animate-switch" ng-switch-when="bar">bar</div>
</div>

Javascript

$scope.items = ['foo', 'bar'];
$scope.parent = {
  selection: $scope.items[0]
}

CSS

.animate-switch-container {
  position:relative;
  height:40px;
  overflow:hidden;
}
.animate-switch {
  padding:10px;

}
.animate-switch.ng-animate {
  transition:opacity 1s ease;

}
.animate-switch.ng-leave.ng-leave-active,
.animate-switch.ng-enter {
  opacity: 0;
}
.animate-switch.ng-leave,
.animate-switch.ng-enter.ng-enter-active {
  opacity: 1;
}

Ce n'est pas le chaînage, mais c'est un travail d'animation directement à l'aide Angulaire du ngAnimate module. Aussi, voici un exemple de sur angulaire du site web.

3voto

Michal Charemza Points 6269

Vous pouvez utiliser .animation de définir des animations qui sont basé sur Javascript. Par exemple, les fonctions que vous définissez comme les valeurs de addClass et removeClass

app.animation('.fade', [function() {
    return {
        addClass: function(element, className, doneFn) {
        },
        removeClass: function(element, className, doneFn) {
        }
    };
}]);

sont appelés par Angulaire lorsqu'il détecte que vous êtes de l'ajout ou de la suppression d'une classe à partir d'un élément, à partir de l'une des méthodes:

  • {{ }} interpoation dans un modèle. E. g. <span class="{{shouldFade ? 'fade' : ''}}">....
  • À l'aide de ng-class dans un modèle. E. g. <span ng-class="{fade: shouldFade}">...
  • À l'aide de l' $animate service dans une directive. E. g. $animate.addClass(element, 'fade') ou $animate.removeClass(element, 'fade')

Quel est le nom de la classe? C'est la classe que je veux appliquer tout en diffusant? La classe que j'attends?

Dans cet exemple, il sera fade. Un peu bizarre, certes, comme dans l'exemple, il est déjà clair que c'est le nom de la classe concernés. Cependant, si dans le même abrégé du cycle, vous êtes à l'ajout de plusieurs classes à la même élément, alors la concaténation d'entre eux sont passés comme cette chaîne.

Qu'est-ce que doneFn censé être? Je suppose que c'est une fonction qui est exécutée une fois l'animation terminée? Ce qui se passe là-dedans?

C'est une fonction que vous appelez une fois quel que soit le Javascript animation que vous définissez est fait. Par exemple, pour définir une animation qui ne fait rien comme tout:

addClass: function(element, className, doneFn) {
  doneFn();
},

L'appelant dit Angulaire que l'animation est terminée. Ce sera, entre autres choses, de supprimer l' ng-animate classe de l'élément.

Que dois-je faire dans le addClass et removeClass fonction alors, si j'ai déjà un doneFn?

Vous mettez en eux un peu de code, peut-être à l'aide de délais d'attente ou une 3ème partie de la bibliothèque, pour changer l'élément en quelque sorte. Lorsque vous avez fini, vous appelez doneFn. Par exemple, un 1 étape d'opacité "animation":

addClass: function(element, className, doneFn) {
  element.css('opacity', 0.5);
  setTimeout(function() {
    doneFn();
  }, 1000);
},

Je voudrais générer un travail d'animation directement à l'aide Angulaire du ngAnimate module, soit avec CSS ou JS.

Ce n'est pas vraiment ont beaucoup à faire avec les réponses ci-dessus! Si je faisais un réel cas, je soupçonne fortement que je de position les éléments absolument, comme rien d'autre (que je pense) au moins, c'est un peu trop compliqué.

Toutefois, si vous ne voulez vraiment de la chaîne d'animations à l'aide de ngAnimate, une voie possible est d'utiliser le fait qu' $animate.addClass et $animate.removeClass retourne une promesse quand il se termine. Afin de chaîne sur la fin d'une telle promesse retourné lors de masquer un élément, il doit être appelé à partir d'un type de situation centrale, et de garder trace de l'élément est visible, masqué, et d'être montré.

Une façon de le faire est d'utiliser 2 des directives sur mesure. Sur chaque élément pour afficher et masquer, qui pourraient être utilisés comme beaucoup ngShow. L'autre sera un parent directive qui permettra à un seul élément à être visible à tout moment, et de la chaîne de suppression de l' ng-hide de la classe (et associés animations) après addition d' ng-hide. Les directives devront communiquer, pourrait être appelé quelque chose comme ngShowUnique et ngShowUniqueController, comme dans l'exemple suivant.

<div ng-show-unique-controller>
  <h1 class="fade" ng-repeat="child in parent.children" ng-show-unique="parent.activeChild == child">@{{child.title}}</h1>
</div>

et ils pourraient être mises en œuvre comme ci-dessous.

app.directive('ngShowUniqueController', function($q, $animate) {
  return {
    controller: function($scope, $element) {
      var elements = [];
      var expressions = [];
      var watchers = [];
      var unregisterWatchers = null;
      var visibleElement = null;

      function registerWatchers() {
        unregisterWatchers = $scope.$watchGroup(expressions, function(vals) {
          var newCurrentIndex = vals.indexOf(true);
          var addPromise;

          if (visibleElement) {
            // Set a fixed height, as there is a brief interval between
            // removal of this class and addition of another
            $element.css('height', $element[0].getBoundingClientRect().height + 'px'); 
            addPromise = $animate.addClass(visibleElement, 'ng-hide');
          } else {
            addPromise = $q.when();
          }
          visibleElement = elements[newCurrentIndex] || null;
          if (!visibleElement) return;

          addPromise.then(function() {
            if (visibleElement) {
              $animate.removeClass(visibleElement, 'ng-hide').then(function() {
                $element.css('height', '');
              });
            }
          })
        });
      }   

      this.register = function(element, expression) {
        if (unregisterWatchers) unregisterWatchers();
        elements.push(element[0]);
        expressions.push(expression);
        registerWatchers();

        // Hide elements initially
        $animate.addClass(element, 'ng-hide');
      };

      this.unregister = function(element) {
        if (unregisterWatchers) unregisterWatchers();
        var index = elements.indexOf(element[0]);
        if (index > -1) {
          elements.splice(index, 1);
          expressions.splice(index, 1);
        }
        registerWatchers();
      };
    } 
  };
});

app.directive('ngShowUnique', function($animate) {
  return {
    require: '^ngShowUniqueController',
    link: function(scope, element, attrs, ngShowUniqueController) {
      ngShowUniqueController.register(element, function() {
        return scope.$eval(attrs.ngShowUnique);
      });

      scope.$on('$destroy', function() {
        ngShowUniqueController.unregister(element);
      });
    }
  };
});

Ceci peut être vu à http://plnkr.co/edit/1eJUou4UaH6bnAN0nJn7?p=preview . Je dois l'avouer, c'est un peu faffy.

1voto

Hitmands Points 1199

à l'aide de ngRepeat qui ne montre qu'un seul élément à la fois, à mon avis, est une mauvaise idée... parce que vous êtes montrant un seul élément! vous pouvez utiliser le parent.activeChild directement la propriété...

Jetez un oeil sur ce qui suit:

Note: je n'ai de cet extrait, en seulement dix minutes, c'est unoptimized et peuvent avoir quelques bug... vous pouvez l'utiliser comme starter :)

(function(window, angular, APP) {
  APP
    .value('menuObject', {
      name: 'Main Navigation',
      current: null,
      children: [{
        label: 'Don\'t ng-show element until ng-hide CSS transition is complete?',
        url: 'http://stackoverflow.com/questions/33336249/dont-ng-show-element-until-ng-hide-css-transition-is-complete',
        isCurrent: false
      },
      {
        label: 'Hitmands - Linkedin',
        url: 'http://it.linkedin.com/in/giuseppemandato',
        isCurrent: false
      },
      {
        label: 'Hitmands - Github',
        url: 'https://github.com/hitmands',
        isCurrent: false
      },
      {
        label: 'Hitmands - StackOverflow',
        url: 'http://stackoverflow.com/users/4099454/hitmands',
        isCurrent: false
      }
  ]})
  .directive('menu', function(menuObject, $q) {
    function menuCtrl($scope, $element) {
      $scope.parent = menuObject;
      
      this.getCurrentChild = function() {
        return $scope.parent.current;
      };
      this.getDomContext = function() {
        return $element;
      };
      this.setCurrentChild = function(child) {
        return $q.when($scope.parent)
        .then(function(parent) {
          parent.current = child;
          return parent;
        })
        .then(function(parent) {
          return parent.children.forEach(function(item) {
            item.isCurrent = child && (item.label === child.label);
          });
        })
      };
    }
    
    return {
      restrict: 'A',
      templateUrl: 'embedded-menutemplate',
      scope: {},
      controller: menuCtrl
    };
  })
  .directive('menuItem', function($animate, $q, $timeout) {
    
    function menuItemPostLink(iScope, iElement, iAttributes, menuCtrl) {
      iElement.bind('click', setCurrentTitle);
      iScope.$on('$destroy', function() {
        iElement.unbind('click', setCurrentTitle);
      })
      
      function setCurrentTitle(event) {
        event.preventDefault();
        var title;
        
        return $q
        .when(menuCtrl.getDomContext())
        .then(function(_menuElement) {
          title = angular.element(
            _menuElement[0].querySelector('#menuItemCurrent')
          );
        })
        .then(function() {
          return title.addClass('fade-out');
        })
        .then(function() {
          return $timeout(menuCtrl.setCurrentChild, 700, true, iScope.child);
        })
        .then(function() {
          return title.removeClass('fade-out');
        })
      }
    }
				
    return {
      require: '^menu',
      link: menuItemPostLink,
      restrict: 'A'
    };
  })
;

})(window, window.angular, window.angular.module('AngularAnimationExample', ['ngAnimate']));
nav {
		text-align: center;
	}
	.link {
		display: inline-block;
		background-color: lightseagreen;
		color: black;
		padding: 5px 15px;
		margin: 1em;
	}
	#menuItemCurrent {
		padding: 1em;
		text-transform: uppercase;
		border: 1px solid black;
	}
	#menuItemCurrent span {
		transition: 500ms opacity linear;
		opacity: 1;
	}
	#menuItemCurrent.fade-out span {
		opacity: 0;
	}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.7/angular-animate.js"></script>

<article ng-app="AngularAnimationExample">
  
  <nav menu></nav>

  <script id="embedded-menutemplate" type="text/ng-template">

	<nav >
		<a menu-item class="link" ng-repeat="child in parent.children track by $index" ng-bind="child.label"  ng-href="{{ child.url }}"></a>
	
		<h1 id="menuItemCurrent"><span  ng-bind="parent.current.url || 'NoMenuCurrentSelected'"></span></h1>	
		{{ parent.current || json }}
	</nav>
	
  </script>
</article>

1voto

pixelbits Points 5710

Le problème est que H1 est un élément de niveau bloc, qui est placé à l'intérieur de son parent et de l'absence de chevauchement est autorisé. C'est pourquoi vous voyez l'un de l'animation qui est en train de disparaître à pousser vers le bas l'animation qui apparaît.

Vous pouvez voir ce qui se passe plus clairement ici: Demo

Pour résoudre ce problème, vous voulez garder le niveau de l'îlot de l'élément H1, et de rendre sa position relative, de sorte qu'il peut garder sa position relative dans le total des flux de la page. Ensuite, la valeur de l'enfant les éléments SPAN avoir un positionnement absolu - position absolue par rapport au parent H1. Cela permet à tous les éléments span chevauchent les uns les autres.

CSS

.fade {
  opacity: 1;
  position: relative;
}

.fade.ng-hide-add {
    transition:opacity 1s ease;
    position: absolute;
}

.fade.ng-hide-remove {
    transition:opacity 1s ease 1s;
    position: absolute;
}

.fade.ng-hide-add {
  opacity:1;
}

.fade.ng-hide-add.ng-hide-add-active {
  opacity:0;
}

.fade.ng-hide-remove {
    opacity:0;
}

.fade.ng-hide-remove.ng-hide-remove-active {
    opacity:1;
}

HTML

  <body ng-controller="MainCtrl">
    <h1><span class="fade" ng-repeat="child in parent.children" ng-show="parent.activeChild == child ">@{{child.title}}</span></h1>
    <button ng-repeat="child in parent.children" ng-click="parent.activeChild = child">{{ child.title }}</button>
  </body>

Il y a un problème mais... Puisque la DURÉE éléments de positionnement absolu, il est supprimé de flux lors de l'animation, et le parent H1 ne peut pas redimensionner pour s'adapter à la DURÉE de contenu. Cela provoque la DURÉE de sauter de façon inattendue.

La façon de régler ce problème (et certes, c'est un peu un hack) est par l'ajout d'un espace vide après la DURÉE de répétition. Ainsi, lorsque le ngRepeat s'étend obtenir retiré de la circulation normale à cause de positionnement absolu, l'espace vide qui est en dehors de la ngRepeat préserve l'espacement de la H1.

Ici, c'est un travail Plunker.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X