diff options
| author | Matias Niemelä | 2013-06-18 13:59:57 -0400 |
|---|---|---|
| committer | Misko Hevery | 2013-07-26 23:49:54 -0700 |
| commit | 81923f1e41560327f7de6e8fddfda0d2612658f3 (patch) | |
| tree | bbf8151bddf4d026f8f5fa3196b84a45ecd9c858 /src/ng/directive | |
| parent | 11521a4cde689c2bd6aaa227b1f45cb3fb53725b (diff) | |
| download | angular.js-81923f1e41560327f7de6e8fddfda0d2612658f3.tar.bz2 | |
feat(ngAnimate): complete rewrite of animations
- ngAnimate directive is gone and was replaced with class based animations/transitions
- support for triggering animations on css class additions and removals
- done callback was added to all animation apis
- $animation and $animator where merged into a single $animate service with api:
- $animate.enter(element, parent, after, done);
- $animate.leave(element, done);
- $animate.move(element, parent, after, done);
- $animate.addClass(element, className, done);
- $animate.removeClass(element, className, done);
BREAKING CHANGE: too many things changed, we'll write up a separate doc with migration instructions
Diffstat (limited to 'src/ng/directive')
| -rw-r--r-- | src/ng/directive/ngClass.js | 118 | ||||
| -rwxr-xr-x | src/ng/directive/ngIf.js | 27 | ||||
| -rw-r--r-- | src/ng/directive/ngInclude.js | 14 | ||||
| -rw-r--r-- | src/ng/directive/ngRepeat.js | 34 | ||||
| -rw-r--r-- | src/ng/directive/ngShowHide.js | 64 | ||||
| -rw-r--r-- | src/ng/directive/ngSwitch.js | 26 |
6 files changed, 141 insertions, 142 deletions
diff --git a/src/ng/directive/ngClass.js b/src/ng/directive/ngClass.js index 75e35a1e..a5b2acb6 100644 --- a/src/ng/directive/ngClass.js +++ b/src/ng/directive/ngClass.js @@ -2,59 +2,72 @@ function classDirective(name, selector) { name = 'ngClass' + name; - return ngDirective(function(scope, element, attr) { - var oldVal = undefined; - - scope.$watch(attr[name], ngClassWatchAction, true); - - attr.$observe('class', function(value) { - var ngClass = scope.$eval(attr[name]); - ngClassWatchAction(ngClass, ngClass); - }); + return ['$animate', function($animate) { + return { + restrict: 'AC', + link: function(scope, element, attr) { + var oldVal = undefined; + + scope.$watch(attr[name], ngClassWatchAction, true); + + attr.$observe('class', function(value) { + var ngClass = scope.$eval(attr[name]); + ngClassWatchAction(ngClass, ngClass); + }); + + + if (name !== 'ngClass') { + scope.$watch('$index', function($index, old$index) { + var mod = $index & 1; + if (mod !== old$index & 1) { + if (mod === selector) { + addClass(scope.$eval(attr[name])); + } else { + removeClass(scope.$eval(attr[name])); + } + } + }); + } - if (name !== 'ngClass') { - scope.$watch('$index', function($index, old$index) { - var mod = $index & 1; - if (mod !== old$index & 1) { - if (mod === selector) { - addClass(scope.$eval(attr[name])); - } else { - removeClass(scope.$eval(attr[name])); + function ngClassWatchAction(newVal) { + if (selector === true || scope.$index % 2 === selector) { + if (oldVal && !equals(newVal,oldVal)) { + removeClass(oldVal); + } + addClass(newVal); } + oldVal = copy(newVal); } - }); - } - function ngClassWatchAction(newVal) { - if (selector === true || scope.$index % 2 === selector) { - if (oldVal && !equals(newVal,oldVal)) { - removeClass(oldVal); + function removeClass(classVal) { + $animate.removeClass(element, flattenClasses(classVal)); } - addClass(newVal); - } - oldVal = copy(newVal); - } - function removeClass(classVal) { - if (isObject(classVal) && !isArray(classVal)) { - classVal = map(classVal, function(v, k) { if (v) return k }); - } - element.removeClass(isArray(classVal) ? classVal.join(' ') : classVal); - } + function addClass(classVal) { + $animate.addClass(element, flattenClasses(classVal)); + } + function flattenClasses(classVal) { + if(isArray(classVal)) { + return classVal.join(' '); + } else if (isObject(classVal)) { + var classes = [], i = 0; + forEach(classVal, function(v, k) { + if (v) { + classes.push(k); + } + }); + return classes.join(' '); + } - function addClass(classVal) { - if (isObject(classVal) && !isArray(classVal)) { - classVal = map(classVal, function(v, k) { if (v) return k }); + return classVal; + }; } - if (classVal) { - element.addClass(isArray(classVal) ? classVal.join(' ') : classVal); - } - } - }); + }; + }]; } /** @@ -70,6 +83,10 @@ function classDirective(name, selector) { * When the expression changes, the previously added classes are removed and only then the * new classes are added. * + * @animations + * add - happens just before the class is applied to the element + * remove - happens just before the class is removed from the element + * * @element ANY * @param {expression} ngClass {@link guide/expression Expression} to eval. The result * of the evaluation can be a string representing space delimited class @@ -78,7 +95,7 @@ function classDirective(name, selector) { * element. * * @example - <example> + <example animations="true"> <file name="index.html"> <input type="button" value="set" ng-click="myVar='my-class'"> <input type="button" value="clear" ng-click="myVar=''"> @@ -86,8 +103,23 @@ function classDirective(name, selector) { <span ng-class="myVar">Sample Text</span> </file> <file name="style.css"> - .my-class { + .my-class-add, + .my-class-remove { + -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; + } + + .my-class, + .my-class-add.my-class-add-active { color: red; + font-size:3em; + } + + .my-class-remove.my-class-remove-active { + font-size:1.0em; + color:black; } </file> <file name="scenario.js"> diff --git a/src/ng/directive/ngIf.js b/src/ng/directive/ngIf.js index c8166ee5..9d99d859 100755 --- a/src/ng/directive/ngIf.js +++ b/src/ng/directive/ngIf.js @@ -30,7 +30,7 @@ * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element * the added class will be lost because the original compiled state is used to regenerate the element. * - * Additionally, you can provide animations via the ngAnimate attribute to animate the **enter** + * Additionally, you can provide animations via the ngAnimate module to animate the **enter** * and **leave** effects. * * @animations @@ -47,36 +47,32 @@ <file name="index.html"> Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/> Show when checked: - <span ng-if="checked" ng-animate="'example'"> + <span ng-if="checked" class="example-if"> I'm removed when the checkbox is unchecked. </span> </file> <file name="animations.css"> - .example-leave, .example-enter { + .example-if.ng-enter, + .example-if.ng-leave { -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; } - .example-enter { + .example-if.ng-enter, + .example-if.ng-leave.ng-leave-active { opacity:0; } - .example-enter.example-enter-active { - opacity:1; - } - .example-leave { + .example-if.ng-enter.ng-enter-active, + .example-if.ng-leave { opacity:1; } - .example-leave.example-leave-active { - opacity:0; - } </file> </example> */ -var ngIfDirective = ['$animator', function($animator) { +var ngIfDirective = ['$animate', function($animate) { return { transclude: 'element', priority: 1000, @@ -84,11 +80,10 @@ var ngIfDirective = ['$animator', function($animator) { restrict: 'A', compile: function (element, attr, transclude) { return function ($scope, $element, $attr) { - var animate = $animator($scope, $attr); var childElement, childScope; $scope.$watch($attr.ngIf, function ngIfWatchAction(value) { if (childElement) { - animate.leave(childElement); + $animate.leave(childElement); childElement = undefined; } if (childScope) { @@ -99,7 +94,7 @@ var ngIfDirective = ['$animator', function($animator) { childScope = $scope.$new(); transclude(childScope, function (clone) { childElement = clone; - animate.enter(clone, $element.parent(), $element); + $animate.enter(clone, $element.parent(), $element); }); } }); diff --git a/src/ng/directive/ngInclude.js b/src/ng/directive/ngInclude.js index 72b5af08..d5ed1fc5 100644 --- a/src/ng/directive/ngInclude.js +++ b/src/ng/directive/ngInclude.js @@ -23,9 +23,6 @@ * (e.g. ngInclude won't work for cross-domain requests on all browsers and for `file://` * access on some browsers) * - * Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter** - * and **leave** effects. - * * @animations * enter - happens just after the ngInclude contents change and a new DOM element is created and injected into the ngInclude container * leave - happens just after the ngInclude contents change and just before the former contents are removed from the DOM @@ -143,8 +140,8 @@ * @description * Emitted every time the ngInclude content is reloaded. */ -var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', '$animator', '$sce', - function($http, $templateCache, $anchorScroll, $compile, $animator, $sce) { +var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', '$animate', '$sce', + function($http, $templateCache, $anchorScroll, $compile, $animate, $sce) { return { restrict: 'ECA', terminal: true, @@ -154,7 +151,6 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile' autoScrollExp = attr.autoscroll; return function(scope, element, attr) { - var animate = $animator(scope, attr); var changeCounter = 0, childScope; @@ -163,7 +159,7 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile' childScope.$destroy(); childScope = null; } - animate.leave(element.contents(), element); + $animate.leave(element.contents()); }; scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) { @@ -175,11 +171,11 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile' if (childScope) childScope.$destroy(); childScope = scope.$new(); - animate.leave(element.contents(), element); + $animate.leave(element.contents()); var contents = jqLite('<div/>').html(response).contents(); - animate.enter(contents, element); + $animate.enter(contents, element); $compile(contents)(childScope); if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) { diff --git a/src/ng/directive/ngRepeat.js b/src/ng/directive/ngRepeat.js index e0b2cb38..8f12b7c2 100644 --- a/src/ng/directive/ngRepeat.js +++ b/src/ng/directive/ngRepeat.js @@ -20,9 +20,6 @@ * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). | * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). | * - * Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter**, - * **leave** and **move** effects. - * * * # Special repeat start and end points * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending @@ -131,46 +128,40 @@ I have {{friends.length}} friends. They are: <input type="search" ng-model="q" placeholder="filter friends..." /> <ul> - <li ng-repeat="friend in friends | filter:q" - ng-animate="{enter: 'example-repeat-enter', - leave: 'example-repeat-leave', - move: 'example-repeat-move'}"> + <li class="animate-repeat" ng-repeat="friend in friends | filter:q"> [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old. </li> </ul> </div> </file> <file name="animations.css"> - .example-repeat-enter, - .example-repeat-leave, - .example-repeat-move { + .animate-repeat { -webkit-transition:all linear 0.5s; -moz-transition:all linear 0.5s; - -ms-transition:all linear 0.5s; -o-transition:all linear 0.5s; transition:all linear 0.5s; } - .example-repeat-enter { + .animate-repeat.ng-enter { line-height:0; opacity:0; } - .example-repeat-enter.example-repeat-enter-active { + .animate-repeat.ng-enter.ng-enter-active { line-height:20px; opacity:1; } - .example-repeat-leave { + .animate-repeat.ng-leave { opacity:1; line-height:20px; } - .example-repeat-leave.example-repeat-leave-active { + .animate-repeat.ng-leave.ng-leave-active { opacity:0; line-height:0; } - .example-repeat-move { } - .example-repeat-move.example-repeat-move-active { } + .animate-repeat.ng-move { } + .animate-repeat.ng-move.ng-move-active { } </file> <file name="scenario.js"> it('should render initial data set', function() { @@ -195,7 +186,7 @@ </file> </example> */ -var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) { +var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { var NG_REMOVED = '$$NG_REMOVED'; var ngRepeatMinErr = minErr('ngRepeat'); return { @@ -204,7 +195,6 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) { terminal: true, compile: function(element, attr, linker) { return function($scope, $element, $attr){ - var animate = $animator($scope, $attr); var expression = $attr.ngRepeat; var match = expression.match(/^\s*(.+)\s+in\s+(.*?)\s*(\s+track\s+by\s+(.+)\s*)?$/), trackByExp, trackByExpGetter, trackByIdFn, trackByIdArrayFn, trackByIdObjFn, lhs, rhs, valueIdentifier, keyIdentifier, @@ -316,7 +306,7 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) { for (key in lastBlockMap) { if (lastBlockMap.hasOwnProperty(key)) { block = lastBlockMap[key]; - animate.leave(block.elements); + $animate.leave(block.elements); forEach(block.elements, function(element) { element[NG_REMOVED] = true}); block.scope.$destroy(); } @@ -342,7 +332,7 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) { // do nothing } else { // existing item which got moved - animate.move(block.elements, null, jqLite(previousNode)); + $animate.move(block.elements, null, jqLite(previousNode)); } previousNode = block.endNode; } else { @@ -360,7 +350,7 @@ var ngRepeatDirective = ['$parse', '$animator', function($parse, $animator) { if (!block.startNode) { linker(childScope, function(clone) { - animate.enter(clone, null, jqLite(previousNode)); + $animate.enter(clone, null, jqLite(previousNode)); previousNode = clone; block.scope = childScope; block.startNode = clone[0]; diff --git a/src/ng/directive/ngShowHide.js b/src/ng/directive/ngShowHide.js index 7ef7008c..bdbcf463 100644 --- a/src/ng/directive/ngShowHide.js +++ b/src/ng/directive/ngShowHide.js @@ -12,8 +12,6 @@ * With ngHide this is the reverse whereas true values cause the element itself to become * hidden. * - * Additionally, you can also provide animations via the ngAnimate attribute to animate the **show** - * and **hide** effects. * * @animations * show - happens after the ngShow expression evaluates to a truthy value and the contents are set to visible @@ -29,36 +27,37 @@ Click me: <input type="checkbox" ng-model="checked"><br/> <div> Show: - <span class="check-element" - ng-show="checked" - ng-animate="{show: 'example-show', hide: 'example-hide'}"> + <span class="check-element example-show-hide" ng-show="checked"> <span class="icon-thumbs-up"></span> I show up when your checkbox is checked. </span> </div> <div> Hide: - <span class="check-element" - ng-hide="checked" - ng-animate="{show: 'example-show', hide: 'example-hide'}"> + <span class="check-element example-show-hide" ng-hide="checked"> <span class="icon-thumbs-down"></span> I hide when your checkbox is checked. </span> </div> </file> <file name="animations.css"> - .example-show, .example-hide { + .example-show-hide { -webkit-transition:all linear 0.5s; -moz-transition:all linear 0.5s; -ms-transition:all linear 0.5s; -o-transition:all linear 0.5s; transition:all linear 0.5s; + display:block; + } + .example-show-hide.ng-hide { + display:none; } - .example-show { + .example-show-hide.ng-hide-remove { + display:block; line-height:0; opacity:0; padding:0 10px; } - .example-show-active.example-show-active { + .example-show-hide.ng-hide-remove.ng-hide-remove-active { line-height:20px; opacity:1; padding:10px; @@ -66,14 +65,14 @@ background:white; } - .example-hide { + .example-show-hide.ng-hide-add { line-height:20px; opacity:1; padding:10px; border:1px solid black; background:white; } - .example-hide-active.example-hide-active { + .example-show-hide.ng-hide-add.ng-hide-add-active { line-height:0; opacity:0; padding:0 10px; @@ -98,12 +97,10 @@ </file> </example> */ -//TODO(misko): refactor to remove element from the DOM -var ngShowDirective = ['$animator', function($animator) { +var ngShowDirective = ['$animate', function($animate) { return function(scope, element, attr) { - var animate = $animator(scope, attr); scope.$watch(attr.ngShow, function ngShowWatchAction(value){ - animate[toBoolean(value) ? 'show' : 'hide'](element); + $animate[toBoolean(value) ? 'show' : 'hide'](element); }); }; }]; @@ -121,9 +118,6 @@ var ngShowDirective = ['$animator', function($animator) { * With ngHide this is the reverse whereas true values cause the element itself to become * hidden. * - * Additionally, you can also provide animations via the ngAnimate attribute to animate the **show** - * and **hide** effects. - * * @animations * show - happens after the ngHide expression evaluates to a non truthy value and the contents are set to visible * hide - happens after the ngHide expression evaluates to a truthy value and just before the contents are set to hidden @@ -138,36 +132,36 @@ var ngShowDirective = ['$animator', function($animator) { Click me: <input type="checkbox" ng-model="checked"><br/> <div> Show: - <span class="check-element" - ng-show="checked" - ng-animate="{show: 'example-show', hide: 'example-hide'}"> + <span class="check-element example-show-hide" ng-show="checked"> <span class="icon-thumbs-up"></span> I show up when your checkbox is checked. </span> </div> <div> Hide: - <span class="check-element" - ng-hide="checked" - ng-animate="{show: 'example-show', hide: 'example-hide'}"> + <span class="check-element example-show-hide" ng-hide="checked"> <span class="icon-thumbs-down"></span> I hide when your checkbox is checked. </span> </div> </file> <file name="animations.css"> - .example-show, .example-hide { + .example-show-hide { -webkit-transition:all linear 0.5s; -moz-transition:all linear 0.5s; - -ms-transition:all linear 0.5s; -o-transition:all linear 0.5s; transition:all linear 0.5s; + display:block; + } + .example-show-hide.ng-hide { + display:none; } - .example-show { + .example-show-hide.ng-hide-remove { + display:block; line-height:0; opacity:0; padding:0 10px; } - .example-show.example-show-active { + .example-show-hide.ng-hide-remove.ng-hide-remove-active { line-height:20px; opacity:1; padding:10px; @@ -175,14 +169,14 @@ var ngShowDirective = ['$animator', function($animator) { background:white; } - .example-hide { + .example-show-hide.ng-hide-add { line-height:20px; opacity:1; padding:10px; border:1px solid black; background:white; } - .example-hide.example-hide-active { + .example-show-hide.ng-hide-add.ng-hide-add-active { line-height:0; opacity:0; padding:0 10px; @@ -207,12 +201,10 @@ var ngShowDirective = ['$animator', function($animator) { </file> </example> */ -//TODO(misko): refactor to remove element from the DOM -var ngHideDirective = ['$animator', function($animator) { +var ngHideDirective = ['$animate', function($animate) { return function(scope, element, attr) { - var animate = $animator(scope, attr); scope.$watch(attr.ngHide, function ngHideWatchAction(value){ - animate[toBoolean(value) ? 'hide' : 'show'](element); + $animate[toBoolean(value) ? 'hide' : 'show'](element); }); }; }]; diff --git a/src/ng/directive/ngSwitch.js b/src/ng/directive/ngSwitch.js index f36e651c..38a123a2 100644 --- a/src/ng/directive/ngSwitch.js +++ b/src/ng/directive/ngSwitch.js @@ -19,9 +19,6 @@ * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default * attribute is displayed. * - * Additionally, you can also provide animations via the ngAnimate attribute to animate the **enter** - * and **leave** effects. - * * @animations * enter - happens after the ngSwtich contents change and the matched child element is placed inside the container * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM @@ -55,9 +52,8 @@ <tt>selection={{selection}}</tt> <hr/> <div - class="example-animate-container" - ng-switch on="selection" - ng-animate="{enter: 'example-enter', leave: 'example-leave'}"> + class="example-animate-container animate-switch" + ng-switch on="selection"> <div ng-switch-when="settings">Settings Div</div> <div ng-switch-when="home">Home Span</div> <div ng-switch-default>default</div> @@ -71,10 +67,9 @@ } </file> <file name="animations.css"> - .example-leave, .example-enter { + .animate-switch > * { -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; -moz-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; - -ms-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; -o-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s; @@ -90,17 +85,17 @@ padding:10px; } - .example-enter { + .animate-switch > .ng-enter { top:-50px; } - .example-enter.example-enter-active { + .animate-switch > .ng-enter.ng-enter-active { top:0; } - .example-leave { + .animate-switch > .ng-leave { top:0; } - .example-leave.example-leave-active { + .animate-switch > .ng-leave.ng-leave-active { top:50px; } </file> @@ -119,7 +114,7 @@ </file> </example> */ -var ngSwitchDirective = ['$animator', function($animator) { +var ngSwitchDirective = ['$animate', function($animate) { return { restrict: 'EA', require: 'ngSwitch', @@ -129,7 +124,6 @@ var ngSwitchDirective = ['$animator', function($animator) { this.cases = {}; }], link: function(scope, element, attr, ngSwitchController) { - var animate = $animator(scope, attr); var watchExpr = attr.ngSwitch || attr.on, selectedTranscludes, selectedElements, @@ -138,7 +132,7 @@ var ngSwitchDirective = ['$animator', function($animator) { scope.$watch(watchExpr, function ngSwitchWatchAction(value) { for (var i= 0, ii=selectedScopes.length; i<ii; i++) { selectedScopes[i].$destroy(); - animate.leave(selectedElements[i]); + $animate.leave(selectedElements[i]); } selectedElements = []; @@ -153,7 +147,7 @@ var ngSwitchDirective = ['$animator', function($animator) { var anchor = selectedTransclude.element; selectedElements.push(caseElement); - animate.enter(caseElement, anchor.parent(), anchor); + $animate.enter(caseElement, anchor.parent(), anchor); }); }); } |
