问题
I'm playing with transitions and directives. I've created a Card directive that should show a clone of it self in fullscreen when clicked. The transition doesn't happen if I don't apply the altering css class in a timeout. Is that how it should be done?
<div ng-app='trans'>
<div data-card class='card'>timeout</div>
<div data-card='notimeout' class='card'>not timeout</div>
</div>
Between to original position and the fullscreen mode it should transition with a spin. The goto class is just so that i can add/remove transitions so that the card doesn't transition widht/height when the window is resized. I think it reads nice too =)
.card {
width:10vh;
height:14vh;
background-color:pink;
margin: 10px;
}
.card.goto.fullscreen {
transition: all 0.6s linear;
}
.card.fullscreen {
height:95vh;
width: 68vh;
position:absolut;
position: absolute;
top: 50% !important;
left: 50% !important;
margin: 0;
transform: translate(-50%, -50%) rotateY(360deg);
}
This is a simplified version of my directive.
var app = angular.module('trans', []);
app.directive('card', ['$document', '$timeout', function ($document, $timeout) {
return {
restrict: 'A',
link: link,
scope: {}
};
function link(scope, element, attrs) {
var clone;
element.on('click', function () {
if (clone) {
clone.off().remove();
}
clone = element.clone();
var spec = getCardSpecifications();
clone.css({
'margin': '0',
'top': spec.top + 'px',
'left': spec.left + 'px',
'position': 'absolute'
});
$document.find('body').append(clone);
clone.addClass('goto');
if (attrs.card == 'notimeout') {
clone.addClass('fullscreen');
} else {
$timeout(function () {
clone.addClass('fullscreen');
}, 0);
}
});
function getCardSpecifications() {
var spec = {};
spec.top = element.prop('offsetTop');
spec.left = element.prop('offsetLeft');
spec.height = element[0].offsetHeight;
spec.width = element[0].offsetWidth;
return spec;
}
}
}]);
I've created this jsfiddle that demonstrates the problem.
回答1:
The problem doesn't have anything to do with Angular itself, but with creating a new DOM node and setting a class on it right after. Such a problem is described e.g. here, and it uses the same solution as yours in the first example.
DISCLAIMER: The real Angular way of doing this would be ngAnimate. What follows is a solution that is almost the same as the OP's, and one you'd only want to use if you don't want to depend on that module – but it's only ~11kb uncompressed, and 4kb gzipped. Choose wisely!
What also worked for me is waiting for the DOM node to be ready:
clone.ready(function() {
clone.addClass('fullscreen');
});
This amounts to almost the same thing as using a 0ms timeout, but is a. more descriptive and b. works in all cases, while the timeout solution apparently sometimes fails in Firefox (see linked article).
The second solution given in the article also reads a little more hackish (matter of opinion, really), and you'll have to retrieve the actual DOM element instead of the jqLite wrapper around it to use it.
Why exactly this happens, even though you are adding the class "after appending", I wasn't able to quickly find out. Perhaps appendChild
, which append
most likely uses internall, is asynchronous (i.e. pushes the DOM manipulation task onto the event queue)? Some more googling might be useful if you're really interested in the cause of this problem.
回答2:
You should probably use animate to do an animation
$animate.addClass(clone, 'fullscreen'
I had issues trying to get the dependency for animate in fiddle so
I made a Plunker
回答3:
When changing the DOM via the directive with methods like css, you have to inform the digest loop of these changes.
In order to accomplish that you should add scope.$apply() after adding the css class inside your if statement. The reason why $timeout works for you is that because it calls $apply after the timeout executes.
来源:https://stackoverflow.com/questions/26575237/css-transition-not-showing-with-directive