Das Hinzufügen von „Animationen“ ist eine effektive Möglichkeit, Benutzern das Verhalten der Anwendung bewusst zu machen. „Liste“ ist das am häufigsten verwendete Schnittstellenformular in Anwendungen. Es gibt häufig Vorgänge wie das Hinzufügen von Zeilen, das Löschen von Zeilen und das Verschieben von Zeilen. Stellen Sie sich vor, der Vorgang des Hinzufügens ist sehr einfach. Beim Löschen geht er von groß nach klein und verschwindet dann. Beim Verschieben geht er von klein nach groß. Es fühlt sich nicht kompliziert an und sollte mithilfe des CSS-Übergangs erfolgen. In der Praxis haben wir jedoch festgestellt, dass es viele Probleme zu lösen gilt.
Machen wir ein paar einfache Tests
1. Erstversion
<div class='list'> <div class='row-1'>row-1</div> <div class='row-2'>row-2</div> </div>
.list{margin:20px;background:#eee;font-size:18px;color:white;} .row-1{background:green;overflow:hidden;padding:15px;} .row-2{background:blue;padding:15px;} /*demo1*/ .demo-1 .remove{-webkit-transition: height 3s linear;} .demo-1 .remove.active{height:0;}
var ele = document.querySelector('.demo-1 .row-1'); ele.classList.add('remove'); ele.classList.add('active');
Die Idee ist sehr einfach. Legen Sie den Effekt der Animation fest, indem Sie die Klasse „remove“ hinzufügen, fügen Sie „active“ hinzu, um das CSS-Attribut zu ändern und die Animation zu aktivieren.
Das Ergebnis unterscheidet sich von dem, was ich erwartet habe. Es gibt zwei Probleme: 1. Die Animation wird nicht ausgeführt. 2. Zeile 1 verschwindet nicht. Warum? Erstens kann der CSS-Übergang nicht auf das Auto-Attribut einwirken, da für Zeile 1 ursprünglich keine Höhe festgelegt ist und daher keine Animation von der vorhandenen Höhe auf 0 generiert wird. Zweitens setzt height=0 nur den Inhaltsbereich auf 0 und der Abstand hat sich nicht geändert, sodass Zeile-1 immer noch 30 Pixel Platz einnimmt.
2. Geben Sie eine feste Höhe an und fügen Sie der Polsterung eine Animation hinzu
CSS anpassen
/*demo2*/ .demo-2 .row-1{height:48px;} .demo-2 .remove{-webkit-transition: height 3s linear, padding-top 3s linear;} .demo-2 .row-1.remove.active{height:0;padding-top:0;padding-bottom:0;}
Der Effekt ist dieses Mal korrekt, Zeile 1 geht von 48px auf 0 und auch der Abstand ändert sich entsprechend.
3. Gibt es eine andere Möglichkeit? Muss ich die Höhe angeben? Ist die Transformation in Ordnung?
CSS ändern
/*demo3*/ .demo-3 .remove{-webkit-transition: -webkit-transform 3s linear,padding 0s linear 3s;} .demo-3 .row-1.remove.active{-webkit-transform-origin:0 0;-webkit-transform:scaleY(0);}
Auch wenn die Höhe nicht festgelegt ist, ist die Animation durch Transformation kein Problem. Das Problem besteht darin, dass Zeile 1 immer noch an ihrem ursprünglichen Platz ist und immer noch Platz einnimmt und Zeile 2 nicht nach oben verschoben wurde. Dies führt zu einem Problem, nachdem die Animation ausgeführt wurde (einschließlich des zweiten Beispiels zum Festlegen der Höhe). Zeile 1 wird nicht gelöscht, sondern ist unsichtbar.
4. Lösen Sie das Problem des Löschens von Elementen nach der Animationsausführung
CSS ändern
JS ändern
var ele, l; ele = document.querySelector('.demo-4 .row-1'); l = ele.addEventListener('webkitTransitionEnd', function(evt){ if (evt.propertyName === 'height') { ele.style.display = 'none'; ele.style.height = ''; ele.removeEventListener('webkitTransitionEnd', l, false); } }, false); ele.style.height = ele.offsetHeight + 'px'; ele.classList.add('remove'); $timeout(function(){ ele.classList.add('active'); ele.style.height = '0px'; });
Der Effekt ist dieses Mal gut. Es sind einige Dinge zu beachten: 1. Durch die Registrierung des TransitionEnd-Ereignisses können Sie das Ende der Animation erfassen. 2. Sie können mehrere Animationen gleichzeitig ausführen. Am Ende jedes Dings wird ein TransitionEnd-Ereignis generiert. Sie können anhand des „propertyName“ des Ereignisses erkennen, um welche Eigenschaft es sich handelt. Die Animation ist beendet.
5. Ich habe auch versucht, Velocity.js zu verwenden
Keine Notwendigkeit, CSS festzulegen
JS-Code
var ele = document.querySelector('.demo-5 .row-1'); Velocity(ele, 'slideUp', { duration: 1000 });
Mit Blick auf den Ausführungsprozess habe ich auch die Höhe und die Polsterung geändert. Velocity verwendet jedoch die Funktion requestAnimationFrame. Ich denke, wenn die Animation relativ einfach ist, besteht keine Notwendigkeit, andere Bibliotheken einzuführen, und der Laufeffekt beim direkten Schreiben ist fast der gleiche.
6. Da nun die Höhe klar ist, wie sieht es mit der Änderung der Breite aus?
CSS anpassen
.demo-6 .row-1{width:100%;} .demo-6 .remove{-webkit-transition: width 3s linear;} .demo-6 .row-1.remove.active{width:0%;}
Obwohl die Breite selbst prozentual eingestellt werden kann, besteht weiterhin das Problem der nicht festgelegten Höhe.
7. Verwenden Sie JS, um das Problem der Breitenänderung zu lösen
CSS festlegen
.demo-7 .row-1{width:100%;height:48px;} .demo-7 .remove{-webkit-transition: width 3s linear, opacity 3s ease;} .demo-7 .row-1.remove.active{width:0%;opacity:0;}
固定了height已有动效正常了。其他的改进可参照前面的例子了。
二、一个完整的例子
完整的例子实在angular中实现的。angular实现首先一个问题就是在什么时机设置动效?因为,angular是双向绑定的,如果在controller中删除了一个对象,渲染界面的时候这个对象就没了,所以必须介入到数据绑定的过程中。angular提供ngAnimatie这个动画模块,试了一下它也确实可以完成ngRepeat列表数据更新的动效。但是要额外引入angular-animation.js,虽然不大,还是觉得不是很有必要。另外,我是在一个已经写好的框架页面上加动画,如果需要引入新的module,需要改框架文件,我觉得不好。试了试动态加载animation模块也没成功,所以就研究了一下自己怎么控制动效。
angular即使不加载animation模块,也有一个$animate,它为动效控制留出了接口。
看JS
var fnEnter = $animate.enter, fnLeave = $animate.leave; $animate.enter = function() { var defer = $q.defer(), e = arguments[0], p = arguments[1], a = arguments[2], options = { addClass: 'ng-enter' }; fnEnter.call($animate, e, p, a, options).then(function() { $animate.addClass(e, 'ng-enter-active').then(function(){ var l = e[0].addEventListener('webkitTransitionEnd', function(){ e[0].classList.remove('ng-enter-active'); e[0].classList.remove('ng-enter'); e[0].removeEventListener('webkitTransitionEnd', l, false); defer.resolve(); }, false); }); }); return defer.promise; }; $animate.leave = function() { var defer = $q.defer(), e = arguments[0]; $animate.addClass(e, 'ng-leave').then(function(){ $animate.addClass(e, 'ng-leave-active').then(function(){ var l = e[0].addEventListener('webkitTransitionEnd', function(){ fnLeave.call($animate, e).then(function(){ defer.resolve(); }); }, false); }); }); return defer.promise; };
ng-repeat进行数据更新是会调用$animate服务的enters,leave和move方法,所以,要自己控制动效就要重写对应的方法。重写的时候要用$animate添加,直接在dom上设置有问题。(这一段的angular的逻辑比较底层,没有太看明白,还需要深入研究。)
另外,在移动行的位置时,要通过$timeout将删除和插入放到两个digest循环中处理,否则看不出效果。
var index = records.indexOf($scope.selected), r = records.splice(index, 1); $timeout(function(){ records.splice(index + 1, 0, r[0]); },500);
angular的动画和digest循环关系密切,看了angular-animation.js的代码没看明白,还需要深入研究才行。