从指令内部动态设置 class 无法与 ng-animate 一起正常工作
Setting class dinamically from inside a directive not working properly with ng-animate
我正在加载 this public API 中的项目列表。加载列表后,我使用以下方法对元素应用过渡:
/* ng-active */
.ng-enter{
opacity: 0.2;
padding-left: 30px;
transition: all ease 1000ms;
}
.ng-enter-active{
opacity: 1.0 ;
padding-left: 0px ;
}
.ng-enter-stagger{
transition-delay: 0.9s ;
-webkit-transition-delay: 0.9s ;
transition-duration: 0s ;
-webkit-transition-duration: 0s ;
}
但是,我需要在指令中更改某些项目中的 backgorund-color
。为此,我使用 ng-class
来调用一个函数,该函数使一些逻辑可以应用特定的 class。这是指令:
angular.module('myApp').directive('list', function() {
return {
restrict: 'E',
template: ''+
'<div id="{{$index}}" ng-repeat="value in values track by $index"'+
'ng-class="getCSSClass(value.suite)">'+
'<PRE><span class="id">{{value.id}}</span>'+
'<span class="suite">({{value.suite}})</span>'+
'<span class="aspace">°</span>[{{value.name}} , {{value.username}}] <br />'+
'<span class="phone">{{value.phone}}</span><br/>'+
'<span class="email">{{value.email}}, {{value.website}}</span></PRE>'+
'</div>'+
'',
replace: true,
controller: function($scope) {
$scope.getCSSClass = function(suite) {
return suite <= 500 ? "postBody backgr" : "postBody";
}
}
}
});
但是在 backgorund-color
发生变化的项目中,动画应用不正确。这是一个 plnkr 您可以看到问题的地方。
我需要的是列表中的所有项目都可以按顺序动画。
好吧,我似乎无法理解 class 的问题,但是您可以通过在工厂中定义它来使用样式并直接在样式中绑定它而不是 class。有用。
angular.module('myApp', ['ngAnimate', 'ui.bootstrap'])
.factory('dataService', function($http, $filter) {
return {
get: function() {
var url = "https://jsonplaceholder.typicode.com/users";
var events = [];
return $http.get($filter('trusted')(url)).then(function(json) {
angular.forEach(json.data, function(value, key) {
var suite = value.address.suite.replace(/([^0-9]+)([0-9]+)/g, "");
var obj = {
'id': value.id,
'name': value.name,
'username': value.username,
'email': value.email,
'phone': value.phone,
'website': value.website,
'suite': suite,
'style': suite <= 500 ? 'background-color: #FFEBBE; border: 1px dotted; border-color: #98bf21' : '',
'state': false
};
events.push(obj); // Push en el array
});
return Promise.resolve(events);
});
}
}
})
.directive('list', function() {
return {
restrict: 'E',
template: '' +
'<div id="{{$index}}" ng-repeat="value in values track by $index"' +
'style="{{value.style}}">' +
'<PRE><span class="id">{{value.id}}</span>' +
'<span class="suite">({{value.suite}})</span>' +
'<span class="aspace">°</span>[{{value.name}} , {{value.username}}] <br />' +
'<span class="phone">{{value.phone}}</span><br/>' +
'<span class="email">{{value.email}}, {{value.website}}</span></PRE>' +
'</div>' +
'',
replace: true,
controller: function($scope) {
}
}
})
.directive('loading', function() {
return {
restrict: 'E',
replace: true,
template: '<div>' +
'<br /><h2 class="blink">Getting Data from Server ...</h2>' +
'<div class="loader">' +
'<div class="dot"></div>' +
'<div class="dot"></div>' +
'<div class="dot"></div>' +
'</div>' +
'</div>',
link: function(scope, element, attr) {
scope.$watch('_loading', function(val) {
if (val) {
element.css('display', 'block');
} else {
element.css('display', 'none');
}
});
}
}
})
.filter('trusted', ['$sce', function($sce) {
return function(url) {
return $sce.trustAsResourceUrl(url);
};
}])
.controller('PostsAjaxController', function($scope, dataService) {
$scope.values = [];
$scope._loading = true;
setTimeout(function() {
getData();
}, 1000);
// The method
function getData() {
dataService.get().then(function handleResolve(resp) {
$scope.values = resp;
$scope._loading = false;
});
}
});
/* ng-active */
.ng-enter {
opacity: 0.2;
padding-left: 30px;
transition: all ease 1000ms;
}
.ng-enter-active {
opacity: 1.0;
padding-left: 0px;
}
.ng-enter-stagger {
transition-delay: 0.9s;
-webkit-transition-delay: 0.9s;
/*
FROM THE DOCUMENTATION:
In case the stagger doesn't work then these two values must be set
to 0 to avoid an accidental CSS inheritance.
*/
transition-duration: 0s;
-webkit-transition-duration: 0s;
}
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body {
font-family: "Lucida Console";
font-size: 12px;
background-color: #faf9f9;
height: 250px;
}
.postBody {
width: 350px;
border-bottom: dashed 2px #9b9b9b;
background-color: #faf9f9;
line-height: 1.4;
margin-right: -20px;
}
.backgr,.backgr2 {
background-color: #FFEBBE;
border: 1px dotted;
border-color: #98bf21
}
.postBody:last-of-type {
border-bottom: none;
margin-bottom: 10px;
}
.id {
font-size: 2em;
/*line-height: 1.3;
font-weight: bold;*/
float: left;
margin-right: 1px;
margin-left: 7px;
margin-top: 4px;
}
.phone {
display: inline-block;
}
.suite {
font-size: 1em;
/*line-height: 5.4;*/
font-weight: bold;
float: left;
/*margin-left: 7px;*/
margin-top: 30px;
display: flex;
position: absolute;
}
.aspace {
font-size: 1em;
line-height: 2.7;
font-weight: bold;
float: left;
margin-right: 24px;
margin-top: -7px;
}
.email {
/*margin-left: 17px;
font-style: italic;*/
margin-left: 50px;
font-style: italic;
display: flex;
}
.blink {
animation: blink 1s steps(5, start) infinite;
-webkit-animation: blink 1s steps(5, start) infinite;
}
@keyframes blink {
to {
visibility: hidden;
}
}
@-webkit-keyframes blink {
to {
visibility: hidden;
}
}
/* The loader container */
.loader {
position: absolute;
top: 60%;
left: 60%;
width: 200px;
height: 200px;
margin-top: -100px;
margin-left: -100px;
perspective: 200px;
transform-type: preserve-3d;
}
/* The dot */
.dot {
position: absolute;
top: 50%;
left: 50%;
width: 120px;
height: 120px;
margin-top: -60px;
margin-left: -60px;
border-radius: 100px;
border: 40px outset #1e3f57;
transform-type: preserve-3d;
transform-origin: 50% 50%;
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
background-color: transparent;
animation: dot1 1000ms cubic-bezier(.49, .06, .43, .85) infinite;
}
.dot:nth-child(2) {
width: 140px;
height: 140px;
margin-top: -70px;
margin-left: -70px;
border-width: 30px;
border-color: #447891;
animation-name: dot2;
animation-delay: 75ms;
box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.1);
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
}
.dot:nth-child(3) {
width: 160px;
height: 160px;
margin-top: -80px;
margin-left: -80px;
border-width: 20px;
border-color: #6bb2cd;
animation-name: dot3;
animation-delay: 150ms;
box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.1);
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
}
@keyframes dot1 {
0% {
border-color: #1e3f57;
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
}
50% {
border-color: #1e574f;
transform: rotateX(20deg) rotateY(20deg) rotateZ(50deg) translateZ(0px);
}
100% {
border-color: #1e3f57;
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
}
}
@keyframes dot2 {
0% {
border-color: #447891;
box-shadow: inset 0 0 15px 0 rgba(255, 255, 255, 0.2);
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
}
50% {
border-color: #449180;
box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.8);
transform: rotateX(20deg) rotateY(20deg) rotateZ(50deg) translateZ(0px);
}
100% {
border-color: #447891;
box-shadow: inset 0 0 15px 0 rgba(255, 255, 255, 0.2);
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
}
}
@keyframes dot3 {
0% {
border-color: #6bb2cd;
box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.1);
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
}
50% {
border-color: #6bcdb2;
box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.8);
transform: rotateX(20deg) rotateY(20deg) rotateZ(50deg) translateZ(0px);
}
100% {
border-color: #6bb2cd;
box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.1);
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
}
}
<!DOCTYPE html>
<html ng-app="myApp" ng-csp>
<head>
<link data-require="angular-csp@1.7.5" data-semver="1.7.5" rel="stylesheet" href="https://code.angularjs.org/1.7.5/angular-csp.css" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link rel="stylesheet" href="style.css" />
<!-- <link data-require="bootstrap@3.3.5" data-semver="3.3.5" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" /> -->
<script data-require="angular.js@1.7.5" data-semver="1.7.5" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.5/angular.min.js"></script>
<script data-require="angular-animate.js@1.7.5" data-semver="1.7.5" src="https://code.angularjs.org/1.7.5/angular-animate.min.js"></script>
<script data-require="ui-bootstrap.js@2.5.0" data-semver="2.5.0" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.5.0/ui-bootstrap.min.js"></script>
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript" src="controller.js"></script>
<script type="text/javascript" src="services.js"></script>
<script type="text/javascript" src="directives.js"></script>
<script type="text/javascript" src="filters.js"></script>
</head>
<body ng-controller="PostsAjaxController">
<h1>Hello Plunker!</h1>
<loading></loading>
<list></list>
</body>
</html>
将背景 class 选择器移动到单独的 <div>
元素,这样就不会混淆交错动画:
angular.module('myApp').directive('list', function() {
return {
restrict: 'E',
template: ''+
'<div id="{{$index}}" ng-repeat="value in values track by $index"'+
//'class="postBody" ng-class="{backgr: value.suite<=500}">'+
'class="postBody"><div ng-class="{backgr: value.suite<=500}">'+
'<PRE><span class="id">{{value.id}}</span>'+
'<span class="suite">({{value.suite}})</span>'+
'<span class="aspace">°</span>[{{value.name}} , {{value.username}}] <br />'+
'<span class="phone">{{value.phone}}</span><br/>'+
'<span class="email">{{value.email}}, {{value.website}}</span></PRE>'+
'</div></div>'+
'',
replace: true,
controller: function($scope) {
$scope.getCSSClass = function(suite) {
//return suite <= 500 ? ["postBody", "backgr"] : "postBody";
return suite <= 500 ? "backgr" : "";
}
}
}
});
我正在加载 this public API 中的项目列表。加载列表后,我使用以下方法对元素应用过渡:
/* ng-active */
.ng-enter{
opacity: 0.2;
padding-left: 30px;
transition: all ease 1000ms;
}
.ng-enter-active{
opacity: 1.0 ;
padding-left: 0px ;
}
.ng-enter-stagger{
transition-delay: 0.9s ;
-webkit-transition-delay: 0.9s ;
transition-duration: 0s ;
-webkit-transition-duration: 0s ;
}
但是,我需要在指令中更改某些项目中的 backgorund-color
。为此,我使用 ng-class
来调用一个函数,该函数使一些逻辑可以应用特定的 class。这是指令:
angular.module('myApp').directive('list', function() {
return {
restrict: 'E',
template: ''+
'<div id="{{$index}}" ng-repeat="value in values track by $index"'+
'ng-class="getCSSClass(value.suite)">'+
'<PRE><span class="id">{{value.id}}</span>'+
'<span class="suite">({{value.suite}})</span>'+
'<span class="aspace">°</span>[{{value.name}} , {{value.username}}] <br />'+
'<span class="phone">{{value.phone}}</span><br/>'+
'<span class="email">{{value.email}}, {{value.website}}</span></PRE>'+
'</div>'+
'',
replace: true,
controller: function($scope) {
$scope.getCSSClass = function(suite) {
return suite <= 500 ? "postBody backgr" : "postBody";
}
}
}
});
但是在 backgorund-color
发生变化的项目中,动画应用不正确。这是一个 plnkr 您可以看到问题的地方。
我需要的是列表中的所有项目都可以按顺序动画。
好吧,我似乎无法理解 class 的问题,但是您可以通过在工厂中定义它来使用样式并直接在样式中绑定它而不是 class。有用。
angular.module('myApp', ['ngAnimate', 'ui.bootstrap'])
.factory('dataService', function($http, $filter) {
return {
get: function() {
var url = "https://jsonplaceholder.typicode.com/users";
var events = [];
return $http.get($filter('trusted')(url)).then(function(json) {
angular.forEach(json.data, function(value, key) {
var suite = value.address.suite.replace(/([^0-9]+)([0-9]+)/g, "");
var obj = {
'id': value.id,
'name': value.name,
'username': value.username,
'email': value.email,
'phone': value.phone,
'website': value.website,
'suite': suite,
'style': suite <= 500 ? 'background-color: #FFEBBE; border: 1px dotted; border-color: #98bf21' : '',
'state': false
};
events.push(obj); // Push en el array
});
return Promise.resolve(events);
});
}
}
})
.directive('list', function() {
return {
restrict: 'E',
template: '' +
'<div id="{{$index}}" ng-repeat="value in values track by $index"' +
'style="{{value.style}}">' +
'<PRE><span class="id">{{value.id}}</span>' +
'<span class="suite">({{value.suite}})</span>' +
'<span class="aspace">°</span>[{{value.name}} , {{value.username}}] <br />' +
'<span class="phone">{{value.phone}}</span><br/>' +
'<span class="email">{{value.email}}, {{value.website}}</span></PRE>' +
'</div>' +
'',
replace: true,
controller: function($scope) {
}
}
})
.directive('loading', function() {
return {
restrict: 'E',
replace: true,
template: '<div>' +
'<br /><h2 class="blink">Getting Data from Server ...</h2>' +
'<div class="loader">' +
'<div class="dot"></div>' +
'<div class="dot"></div>' +
'<div class="dot"></div>' +
'</div>' +
'</div>',
link: function(scope, element, attr) {
scope.$watch('_loading', function(val) {
if (val) {
element.css('display', 'block');
} else {
element.css('display', 'none');
}
});
}
}
})
.filter('trusted', ['$sce', function($sce) {
return function(url) {
return $sce.trustAsResourceUrl(url);
};
}])
.controller('PostsAjaxController', function($scope, dataService) {
$scope.values = [];
$scope._loading = true;
setTimeout(function() {
getData();
}, 1000);
// The method
function getData() {
dataService.get().then(function handleResolve(resp) {
$scope.values = resp;
$scope._loading = false;
});
}
});
/* ng-active */
.ng-enter {
opacity: 0.2;
padding-left: 30px;
transition: all ease 1000ms;
}
.ng-enter-active {
opacity: 1.0;
padding-left: 0px;
}
.ng-enter-stagger {
transition-delay: 0.9s;
-webkit-transition-delay: 0.9s;
/*
FROM THE DOCUMENTATION:
In case the stagger doesn't work then these two values must be set
to 0 to avoid an accidental CSS inheritance.
*/
transition-duration: 0s;
-webkit-transition-duration: 0s;
}
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
body {
font-family: "Lucida Console";
font-size: 12px;
background-color: #faf9f9;
height: 250px;
}
.postBody {
width: 350px;
border-bottom: dashed 2px #9b9b9b;
background-color: #faf9f9;
line-height: 1.4;
margin-right: -20px;
}
.backgr,.backgr2 {
background-color: #FFEBBE;
border: 1px dotted;
border-color: #98bf21
}
.postBody:last-of-type {
border-bottom: none;
margin-bottom: 10px;
}
.id {
font-size: 2em;
/*line-height: 1.3;
font-weight: bold;*/
float: left;
margin-right: 1px;
margin-left: 7px;
margin-top: 4px;
}
.phone {
display: inline-block;
}
.suite {
font-size: 1em;
/*line-height: 5.4;*/
font-weight: bold;
float: left;
/*margin-left: 7px;*/
margin-top: 30px;
display: flex;
position: absolute;
}
.aspace {
font-size: 1em;
line-height: 2.7;
font-weight: bold;
float: left;
margin-right: 24px;
margin-top: -7px;
}
.email {
/*margin-left: 17px;
font-style: italic;*/
margin-left: 50px;
font-style: italic;
display: flex;
}
.blink {
animation: blink 1s steps(5, start) infinite;
-webkit-animation: blink 1s steps(5, start) infinite;
}
@keyframes blink {
to {
visibility: hidden;
}
}
@-webkit-keyframes blink {
to {
visibility: hidden;
}
}
/* The loader container */
.loader {
position: absolute;
top: 60%;
left: 60%;
width: 200px;
height: 200px;
margin-top: -100px;
margin-left: -100px;
perspective: 200px;
transform-type: preserve-3d;
}
/* The dot */
.dot {
position: absolute;
top: 50%;
left: 50%;
width: 120px;
height: 120px;
margin-top: -60px;
margin-left: -60px;
border-radius: 100px;
border: 40px outset #1e3f57;
transform-type: preserve-3d;
transform-origin: 50% 50%;
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
background-color: transparent;
animation: dot1 1000ms cubic-bezier(.49, .06, .43, .85) infinite;
}
.dot:nth-child(2) {
width: 140px;
height: 140px;
margin-top: -70px;
margin-left: -70px;
border-width: 30px;
border-color: #447891;
animation-name: dot2;
animation-delay: 75ms;
box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.1);
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
}
.dot:nth-child(3) {
width: 160px;
height: 160px;
margin-top: -80px;
margin-left: -80px;
border-width: 20px;
border-color: #6bb2cd;
animation-name: dot3;
animation-delay: 150ms;
box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.1);
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
}
@keyframes dot1 {
0% {
border-color: #1e3f57;
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
}
50% {
border-color: #1e574f;
transform: rotateX(20deg) rotateY(20deg) rotateZ(50deg) translateZ(0px);
}
100% {
border-color: #1e3f57;
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
}
}
@keyframes dot2 {
0% {
border-color: #447891;
box-shadow: inset 0 0 15px 0 rgba(255, 255, 255, 0.2);
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
}
50% {
border-color: #449180;
box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.8);
transform: rotateX(20deg) rotateY(20deg) rotateZ(50deg) translateZ(0px);
}
100% {
border-color: #447891;
box-shadow: inset 0 0 15px 0 rgba(255, 255, 255, 0.2);
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
}
}
@keyframes dot3 {
0% {
border-color: #6bb2cd;
box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.1);
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
}
50% {
border-color: #6bcdb2;
box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.8);
transform: rotateX(20deg) rotateY(20deg) rotateZ(50deg) translateZ(0px);
}
100% {
border-color: #6bb2cd;
box-shadow: inset 0 0 15px 0 rgba(0, 0, 0, 0.1);
transform: rotateX(24deg) rotateY(20deg) rotateZ(0deg) translateZ(-25px);
}
}
<!DOCTYPE html>
<html ng-app="myApp" ng-csp>
<head>
<link data-require="angular-csp@1.7.5" data-semver="1.7.5" rel="stylesheet" href="https://code.angularjs.org/1.7.5/angular-csp.css" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link rel="stylesheet" href="style.css" />
<!-- <link data-require="bootstrap@3.3.5" data-semver="3.3.5" rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" /> -->
<script data-require="angular.js@1.7.5" data-semver="1.7.5" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.5/angular.min.js"></script>
<script data-require="angular-animate.js@1.7.5" data-semver="1.7.5" src="https://code.angularjs.org/1.7.5/angular-animate.min.js"></script>
<script data-require="ui-bootstrap.js@2.5.0" data-semver="2.5.0" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/2.5.0/ui-bootstrap.min.js"></script>
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript" src="controller.js"></script>
<script type="text/javascript" src="services.js"></script>
<script type="text/javascript" src="directives.js"></script>
<script type="text/javascript" src="filters.js"></script>
</head>
<body ng-controller="PostsAjaxController">
<h1>Hello Plunker!</h1>
<loading></loading>
<list></list>
</body>
</html>
将背景 class 选择器移动到单独的 <div>
元素,这样就不会混淆交错动画:
angular.module('myApp').directive('list', function() {
return {
restrict: 'E',
template: ''+
'<div id="{{$index}}" ng-repeat="value in values track by $index"'+
//'class="postBody" ng-class="{backgr: value.suite<=500}">'+
'class="postBody"><div ng-class="{backgr: value.suite<=500}">'+
'<PRE><span class="id">{{value.id}}</span>'+
'<span class="suite">({{value.suite}})</span>'+
'<span class="aspace">°</span>[{{value.name}} , {{value.username}}] <br />'+
'<span class="phone">{{value.phone}}</span><br/>'+
'<span class="email">{{value.email}}, {{value.website}}</span></PRE>'+
'</div></div>'+
'',
replace: true,
controller: function($scope) {
$scope.getCSSClass = function(suite) {
//return suite <= 500 ? ["postBody", "backgr"] : "postBody";
return suite <= 500 ? "backgr" : "";
}
}
}
});