简单的问题,但我遇到了实施问题。如果我有以下DOM设置:
<h1 class="fade" ng-repeat="child in parent.children" ng-show="parent.activeChild== child ">@{{ child.title }}</h1>
当activeChild
模型的parent
属性发生变化时,如何淡出当前活动的子项,在模型更改之前,然后淡入新的活动状态孩子更改后。
我大致工作,只使用CSS过渡:
.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;
}
}
但是,这最终会产生this problem (Plunkr):
基本上,我想链接我的动画。我已经尝试过阅读ng-animate docs了,但我遇到了提供我想要的效果所需的语法问题。
我已经看到Angular文档有这样的内容:
app.animation('.fade', [function() {
return {
addClass: function(element, className, doneFn) {
},
removeClass: function(element, className, doneFn) {
}
};
}]);
className
?这是我想要在淡入/淡出时应用的课程吗?我想要的课程是什么? doneFn
是什么意思?我认为它是动画完成后运行的功能吗?那里有什么?addClass
,我会在removeClass
和doneFn
函数中执行什么操作?我想直接使用Angular的ngAnimate模块生成一个工作动画,使用CSS或JS。我怎样才能做到这一点?
答案 0 :(得分:11)
为什么每个标题都使用单独的<h1>
。您可以使用单个<h1>
标记来显示标题。
我已经为您的问题创建了一个演示,我已成功完成您的要求。
<强>更新强>
注意,编辑代码以使用ngAnimate
模块。当您使用ngAnimate
模块时,它会在您隐藏元素时创建一个类.ng-hide
,
以下是您应用的控制器
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);
}
}]);
你的html页面应该是这样的,
<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>
我已经使用CSS3来实现淡入和淡出动画,
.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;
}
<强>解释强>
为了达到您的要求,我在angularJS中使用了ng-class
和$timeout
。
你可以看到,我只有一个<h1>
标签来显示你的标题。当我更改标题时,我只需更改它的绑定属性$scope.parent.activeChild
。
我使用了两个范围变量$scope.heading.fadeOutModel
和$scope.heading.fadeInModel
来动态添加和删除类fadeIn
和fadeOut
。
当用户点击更改标题时,我已将类fadeOut
添加到您的标题中。所以,这将显示淡出的动画。我也在app.js中发起了一个函数,changeHeading()
。
你可以看到,我强迫角度等待1000
毫秒来完成淡出动画。在此之后,它会将所选标题替换为新标题并添加类fadeIn
。因此,它将启动淡入动画。
希望这会对你有帮助!!!
答案 1 :(得分:4)
根据选择显示特定元素的更有效的方法是使用ngSwitch
。该指令用于根据范围表达式有条件地交换模板上的DOM结构。这是example。
<强> 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;
}
这不是链接,但它是一个直接使用Angular的ngAnimate模块的工作动画。这里还有一个example of it on angular's website。
答案 2 :(得分:4)
您可以使用.animation
来定义基于Javascript的动画。例如,您定义的函数为addClass
和removeClass
app.animation('.fade', [function() {
return {
addClass: function(element, className, doneFn) {
},
removeClass: function(element, className, doneFn) {
}
};
}]);
Angular在检测到您正在从其中一个方法中添加或删除某个类时调用:
{{ }}
在模板中插值。例如。 <span class="{{shouldFade ? 'fade' : ''}}">....
ng-class
。例如。 <span ng-class="{fade: shouldFade}">...
$animate
服务。例如。 $animate.addClass(element, 'fade')
或$animate.removeClass(element, 'fade')
什么是className?这是我想要在淡入/淡出时应用的课程吗?我期待的课程?
在此示例中,它将是fade
。这有点奇怪,因为在示例中已经清楚这是涉及的类名。但是,如果在同一个摘要周期中,您将多个类添加到同一个元素,则它们的串联将作为此字符串传递。
WhatFn意味着什么?我假设这是一个动画完成后运行的功能?那里有什么?
无论您定义的Javascript动画是什么,它都是您调用的函数。例如,要定义一个不执行任何操作的动画:
addClass: function(element, className, doneFn) {
doneFn();
},
调用它告诉Angular动画已完成。除其他外,这将从元素中删除ng-animate
类。
如果我已经有一个doneFn,我该怎么做addClass和removeClass函数?
你输入了一些代码,可能是使用超时或第三方库,以某种方式更改元素。完成后,请致电doneFn
。例如,1步不透明度“动画”:
addClass: function(element, className, doneFn) {
element.css('opacity', 0.5);
setTimeout(function() {
doneFn();
}, 1000);
},
我想直接使用Angular的ngAnimate模块生成一个工作动画,使用CSS或JS。
这与上面的答案没有太大关系!如果我在做一个真实案例,我强烈怀疑我会绝对定位元素,至少其他任何东西(我能想到的)都有点过于复杂。
但是,如果您确实想要使用ngAnimate链接动画,一种可能的方法是使用$animate.addClass
和$animate.removeClass
在完成时返回一个承诺的事实。为了链接到隐藏元素时返回的这种承诺的结尾,必须从某种中心位置调用它,并跟踪哪个元素可见,被隐藏和被显示。
这样做的一种方法是使用2个自定义指令。一个将显示和隐藏每个元素,可以像ngShow
一样使用。另一个将是一个父指令,它将允许任何时候只能看到一个元素,并在添加ng-hide
之后删除ng-hide
类(以及相关的动画)。指令必须通信,可以称为ngShowUnique
和ngShowUniqueController
,如下例所示。
<div ng-show-unique-controller>
<h1 class="fade" ng-repeat="child in parent.children" ng-show-unique="parent.activeChild == child">@{{child.title}}</h1>
</div>
它们可以如下实现。
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);
});
}
};
});
这可以在http://plnkr.co/edit/1eJUou4UaH6bnAN0nJn7?p=preview看到。我不得不承认,这一切都有点过时了。
答案 3 :(得分:1)
使用 ngRepeat只显示一个元素,在我看来,这是一个坏主意...因为你只显示一个元素! 你可以直接使用parent.activeChild属性......
注意:我在十分钟内完成了这个片段,它没有经过优化,可能会有一些错误......你可以将它用作启动器:)
receiver
(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;
}
答案 4 :(得分:1)
问题是H1是位于其父级内的块级元素,不允许重叠。这就是为什么你看到一个正在消失的动画按下正在出现的动画。
您可以在此处看到这种情况更加清晰:Demo
要解决此问题,您需要保持块级元素H1,并使其位置相对,以便它可以保持其在页面整体流中的相对位置。然后将子SPAN元素设置为具有绝对定位 - 相对于父H1的绝对位置。这允许所有span元素相互重叠。
<强> 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>
虽然存在一个问题...由于SPAN元素具有绝对定位,因此在动画时将其从流中移除,并且父H1无法调整大小以适合SPAN内容。这会导致SPAN意外跳转。
解决这个问题的方法(不可否认,它有点像黑客攻击)是在SPAN转发器之后添加一个空白区域。因此,当ngRepeat SPANS因绝对定位而退出正常流量时,ngRepeat外部的空白空间会保留H1的间距。
这是一个有效的Plunker。
答案 5 :(得分:1)
您可能希望查看所有现代浏览器都支持的transitionend事件。
element.addEventListener('transitionend', callback, false);
答案 6 :(得分:0)
快速回答这个问题 - 为了解决这个问题,我一直把内容放在绝对的位置。这样,当转换发生时,它会保持在相同的位置。
没有别的方法可以解决这个问题,因为如果内容inline
或inline-block
,内容会占用dom中的空间,这就是为什么你会看到跳转直到转换完成的原因