最近我非常喜欢Google I/O 2015 event page,特别是那些不同州之间的过渡动画。我知道他们使用了Polymer,但是我试图在Angular(1.4.1)和Angular-material和ui-router中重新创建这种延迟动画。
基本上我想要实现的是这个工作流程:
这不是一项微不足道的任务,ng-animate不是很有帮助,但我想尽可能多地使用它。一个缺点是,在保证解决之前不会添加css类,另一个缺点是,在某一时刻,新旧状态视图都出现在页面上。
我试图创建这个指令:
(function() {
'use strict';
angular
.module('climbguide')
.directive('cgAnimateElement', cgAnimateElement);
/* @ngInject */
function cgAnimateElement($animate, $rootScope, $state) {
return {
restrict: 'A',
link: linkFunc
};
function linkFunc(scope, el) {
$animate.enter(el, el.parent());
var cleanUp = $rootScope.$on('$stateChangeStart',
function(event, toState, toParams) {
if ($rootScope.stateChangeBypass) {
$rootScope.stateChangeBypass = false;
return;
}
event.preventDefault();
var promise = $animate.leave(el);
promise.then(function() {
$rootScope.stateChangeBypass = true;
$state.go(toState.name, toParams);
});
});
scope.$on('$destroy', function() {
cleanUp();
});
}
}
})();
它基本上做了我想要的,但由于某种原因它只能使用它一个元素 - 我假设因为$ rootScope。$ on(' $ stateChangeStart')以及后来使用$ state.go(toState.name,toParams);.
我还找到了另外两个解决方案,
angular-ui-router-in-out ,它使用CSS,但是等待promises在任何动画发生之前被解析(加载器动画是必要的)
angular-gsapify-router ,它使用javascript动画,并且遇到与上面相同的问题。
我仍然只是学习角度,所以我真的不知道如何以正确的方式做到这一点。你有什么想法?非常感谢。
P.S。:抱歉找不到图书馆的链接,但这是我发给SO的第一篇文章,所以我只能发布2个链接:)
答案 0 :(得分:1)
也许它会对某些人有所帮助,但是我可以用它来处理脏兮兮的黑客攻击,但无论如何它确实如此,我在原帖中所描述的。
我更改了指令,因此可以多次重复使用:
(function() {
'use strict';
angular
.module('climbguide')
.directive('cgAnimateElement', cgAnimateElement);
/* @ngInject */
function cgAnimateElement($animate, delayedRouterService) {
return {
restrict: 'A',
link: linkFunc
};
function linkFunc(scope, el) {
var stateChangeBypass = false;
$animate.enter(el, el.parent());
// Use scope instead of $rootScope, so there is no need to de-register listener
scope.$on('$stateChangeStart',
function(event, toState, toParams) {
if (stateChangeBypass) {
// Resuming transition to the next state broadcasts new $stateChangeStart
// event, so it necessary to bypass it
stateChangeBypass = false;
return;
}
delayedRouterService.holdStateChange(event);
var promise = $animate.leave(el);
promise.then(function() {
stateChangeBypass = true;
delayedRouterService.releaseStateChange(toState, toParams);
});
});
}
}
})();
我创建了处理状态更改的服务 - 在ui-router中阻止和恢复状态更改:
(function() {
'use strict';
angular
.module('climbguide')
.factory('delayedRouterService', delayedRouterService);
/* @ngInject */
function delayedRouterService($state) {
var _runningAnimations = 0;
/**
* Public methods
*
*/
var service = {
holdStateChange: holdStateChange,
releaseStateChange: releaseStateChange
};
return service;
//////////////
/**
* Prevent state change from the first animation
* Store the number of currently running animations
*
* @param event
*/
function holdStateChange(event) {
if (_runningAnimations === 0) {
event.preventDefault();
}
_runningAnimations++;
}
/**
* Remove animation from the stack after it is finished
* Resume state transition after last animation is finished
*
* @param toState
* @param toParams
*/
function releaseStateChange(toState, toParams) {
_runningAnimations--;
if (_runningAnimations === 0) {
$state.go(toState.name, toParams);
}
}
}
})();
因此可以在HTML中为我想要动画的元素使用它
<div class="main" cg-animate-element>
...
</div>
最终的CSS:
.main {
&.ng-animate {
transition: opacity, transform;
transition-duration: 0.4s;
transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1);
}
&.ng-enter {
opacity: 0;
transform: translate3d(0, 100px, 0);
}
&.ng-enter-active {
opacity: 1;
transform: translate3d(0, 0, 0);
}
&.ng-leave {
opacity: 1;
transform: translate3d(0, 0, 0);
}
&.ng-leave-active {
opacity: 0;
transform: translate3d(0, 100px, 0);
}
}