首頁  >  文章  >  web前端  >  給angular加上動畫效遇到的問題總結_AngularJS

給angular加上動畫效遇到的問題總結_AngularJS

WBOY
WBOY原創
2016-05-16 15:15:061389瀏覽

加入「動效」是讓使用者對應用的行為進行感知的一種有效手段。 「列表」是應用程式中最常使用的一種介面形式,經常會有添加行,刪除行,移動行這些操作。設想新增的操作很簡單,刪除時從大到小,然後消失;新增時從小到大;移動就是先刪除再新增。感覺上不複雜,應該利用CSS的transition就能搞定,可是實際做起來發現有不少問題要處理,下面一一來。

來些簡單的測試

1、最初的版本

<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');

想法很簡單,透過加入「remove」類,設定動畫的效果,加入「active」修改css屬性,啟動動畫。

結果和想的不一樣,兩個問題:1、動畫並沒有運行;2、row-1並沒有消失。為什麼?首先,CSS的transition不能作用於auto的屬性,因為row-1本來並沒有設定height,所以不會產生從現有的高度變成0的動畫。第二,height=0只是設定了content區域為0,padding並沒有改變,所以還是row-1還是佔據了30px的空間。

2、指定固定的height且padding也加上動畫

調整CSS

/*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;}

這次的效果是對的,row-1從48px邊到0,同時padding也跟著變。

3、還有沒有別的辦法呢?一定要指定height嗎? transform行不行

修改CSS

/*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);}


即使沒有設定height,透過transform執行動畫也是沒有問題的。問題是,row-1還在原來的地方,還佔空間,row-2並沒有往上挪。由此帶來個問題,動畫執行完了(包括第2個設定height的例子),row-1並沒有刪除掉,只是看不見了。

4、解決動畫執行完清除元素的問題

修改CSS

複製程式碼 程式碼如下:

.demo-4 .remove{-webkit-transition: height 3s linear, padding 3s linear, opacity 3s linear,color .5s linear;}
.demo-4 .row-1.remove.active{padding-top:0;padding-bottom:0;color:rgba(0,0,0,0);opacity:0;}

修改JS

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';
});

這次的效果不錯。有幾個注意的地方:1、透過註冊transitionEnd事件可以捕捉到動結束;2、可以同時執行多個動效,每個東西結束都會產生transitionEnd事件,透過事件的「propertyName」可以知道是哪個屬性的動效結束了。

5.用velocity.js也試了一下

CSS不用設定
JS代碼

var ele = document.querySelector('.demo-5 .row-1');
Velocity(ele, 'slideUp', { duration: 1000 });

看了看執行的過程,也是修改height和padding。但是,velocity用的是requestAnimationFrame函數。我認為如果動效比較簡單,就不用再引入其他的函式庫了,直接寫出來的運作效果差不多。

6.高度搞懂了,變寬度呢?

調整CSS

.demo-6 .row-1{width:100%;}
.demo-6 .remove{-webkit-transition: width 3s linear;}
.demo-6 .row-1.remove.active{width:0%;}

雖然寬本身可以透過百分比進行設置,但是height不固定的問題還是存在。

7.用上JS解決變width的問題

設定CSS

.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的代码没看明白,还需要深入研究才行。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn