display中的transition实现

display中的transition实现

这里讨论的transition,是针对于display:none/block;下的实现或替代性方案。

因为过渡是基于数值和时间来计算的,比如长度、颜色、角度等属性值,是可以在单位时间内变化一定数值,从而达到过渡的动画效果。

像我们常见的opacity/width/height/margin/padding等属性,都是可以应用过渡效果的,然而display这个属性比较尴尬,两种状态,要么显示,要么隐藏,因此无法应用transition效果。

下面探究一下如何变相地实现显隐过渡效果。


1. 原生js实现显隐transition

方案一:visibility+opacity

对于display属性无法应用过渡的问题,可以使用visibility来代替。

虽然visibility也是两种状态hiddenvisible,但这个属性比较特别,hidden相当于0,visible相当于1,从hiddenvisible的转化是可以过渡的,只是并不会有动画效果。

也就是说,设置了transition: visibility 1s linear;,当其visibility值变为hidden时,元素并不会马上消失,而是经过1s后,突然消失,也不会有渐隐的动画效果。

因此,我们需要配合opacity来做到渐隐渐显的过渡效果。

<button id="btn">点击显隐</button>  
<div id="box" class=""><div>

<style>
    #box{
        width: 100px;
        height: 100px;
        background-color: green;
        visibility: visible;
        opacity: 1;
        transition: all 1s linear;
    }
    .hide{
        opacity: 0;
        visibility: hidden;
    }
</style>

<script>
    var box = document.getElementById("box");
    var btn = document.getElementById("btn");
    btn.onclick = function(){
        if(box.className.indexOf("hide") != -1){
            box.className = "";
        }else{
            box.className += "hide";
        }
    };
</script>

两个问题:

  1. 虽然单独使用opacity也可以达到效果,但是这样的盒子,不能透过它去触发下面元素的点击事件,即使它看不见了,也会造成阻碍。而设置了visibilityhidden,就不影响其他元素的点击事件。
  2. 使用这种方案,并不能真正的隐藏元素,依旧会在文档流中占据位置,在页面上留下一块空白。并且visibility属性在IE9及以下的浏览器不支持。


方案二:timeout

前面提到display属性不支持过渡,那么我们可以让其他可见的属性过渡,并与display值的变化分开执行,看起来就像display过渡了一样。

需要注意:

box.style.opacity = 0;
box.style.display = "none";

这样不可行,因为浏览器的执行机制,两步其实会合并执行,元素会直接从文档流中移除,因此根本看不到透明度的过渡变化。

我们可以使用定时器,强制拆开这两步。

<button id="btn">点击显隐</button>  
<div id="box" class=""><div>

<style>
    #box{
        width: 100px;
        height: 100px;
        background-color: green;
        opacity: 1;
        transition: all 1s linear;
    }
</style>

<script>
    var box = document.getElementById("box");
    var btn = document.getElementById("btn");
    btn.onclick = function(){
        if(getComputedStyle(box,null)["display"] == "block"){
            box.style.opacity = 0;
            setTimeout(function(){
                box.style.display = "none";
            },1000);//这里因为要等待过渡的1s,然后才消失
        }else{
            setTimeout(function(){
                box.style.display = "block";
            });//因为紧接着要过渡,所以delay为0
            box.style.opacity = 1;
        }
    };
</script>


方案三:transitionend

transition动画结束时,会触发一个事件,叫做transitionend,我们可以用它替换定时器。

box.style.opacity = 0;
box.addEventListener("transitionend", function(e){
    //propertyName是触发事件相关联的属性,详见MDN
    if(e.propertyName == "opacity"){
        box.style.display = "none";
    }
});



2. jQuery的显隐transition

在jQuery中做到这个过渡效果非常简单,使用animate即可,内部已经自动给我们处理好了。

//toggle是控制显隐的按钮
//form是需要显隐的元素
$('.toggle').click(function(){
    $('.form').animate({
        //'padding-top': 'toggle',
        //'padding-bottom': 'toggle',
        opacity: "toggle",
        height: "toggle" 
    }, 'slow');
});

不需要我们主动判断display的值,只要使用了toggle作为动画效果值,jQuery会自动切换showhide,并且这里还可以看到透明度,高度同时变化。

如果是两个元素连续排列,初始display状态分别为blocknone,只需要给它们设置类名都为form,当点击按钮时,会交替显示元素,并且切换的过渡效果相当自然,常用来做登录和注册的表单显示切换。



3. 框架内部的显隐transition

其实框架内部的过渡动画效果,底层都是用CSS3中transitionanimation实现的。


3.1 Vue的过渡

Vue2.0的过渡是一个内置组件transition元素会把过渡效果应用到其包裹的内容上,而不会额外渲染DOM元素。

使用方式:

data:{show:true}

<input type="button" value="点击隐藏显示" @click="show=!show">

<transition name='fade'>
    <div v-show='show'></div>
</transition>

transition指定namefade后,将会自动生成几个过渡状态的class:

  • .fade-enter{} —— 出现的初始状态
  • .fade-enter-active{} —— 当元素进入,变化过渡的趋向状态
  • .fade-leave{} —— 离开的初始状态(通常不使用)
  • .fade-leave-active{} —— 当元素离开,变化过渡的趋向状态

一般将过渡效果transition设置给active:

.fade-enter-active,.fade-leave-active{
    transition: all .5s linear;
}

.fade-enter{
    opacity: 0;
}

vue过渡状态

当然,我们也可在Vue中使用第三方css动画效果库。

详见Vue2.0过渡及其钩子函数



3.2 Angular的过渡

这里使用的版本是Angular1,用到了angular-animate.js插件。

<html ng-app="myapp">
    <body ng-controller="test" ng-init="showFlag = false">
        <button id="btn">点击显隐</button>  
        <div id="box" class="myanimate" ng-show="showFlag"><div>
    </body>
</html>    

<style>
    #box{
        width: 100px;
        height: 100px;
        background-color: green;
    }
    .myanimate.ng-hide-add,
    .myanimate.ng-hide-remove {
        transition: all linear 0.5s;
    }
    .myanimate.ng-hide{
        opacity: 0;
    }

</style>

<script src="https://cdn.bootcss.com/angular.js/1.7.0/angular.js"></script>
<script src="https://cdn.bootcss.com/angular.js/1.7.0/angular-animate.js"></script>
<script>
    angular.module("myapp",['ngAnimate']).controller("test",function($scope){});
</script>

因为angular-animate插件,在元素向ng-hide的状态切换时,会给该元素自动添加两个class:ng-hide-add/ng-hide-remove,分别代表向ng-hide转换和从ng-hide变为其他状态。

所以我们可以给这两个class设置transition,再给ng-hide设置一个最终的消失状态,这样就有了过渡效果。与上面Vue中的例子类似。

myanimate是我自己加的class,使用了交集选择器,是为了精确定位需要应用过渡效果的元素。

个人感觉Angular中的过渡变化相对于Vue来说比较复杂,不同的指令还有不同的animation class类,需要对照文档来使用。

ng-animate
详细介绍见官方文档

我们也可以使用Angular中现成的第三方css动画效果库:ngAnimate.css,通过增删class很方便地控制过渡效果。


3.3 collapse效果实现

有时候有这样的需求,类似collapse的过渡效果,当点击按钮,下拉选项框会往上收拢消失或向下扩张显示,我们用display的过渡,只能实现透明度的变化,而不能做到高度的过渡效果,因为display:none;的元素宽高都是auto

transition: all 1s linear;的结果就是,经过1秒,元素的透明度由1变为0,紧接着突然消失,然后下面的元素盒子由于有了空间,迅速顶上来,让人觉得很突兀。

我想到的有两种纯css的实现方案:

  1. 正常的设置过渡效果,然后给该过渡元素设置浮动,当元素显示时,下面的盒子需要设置margin-top为该元素的高度,元素隐藏时,给下面盒子移除掉margin-top属性,同时给下面盒子也设置过渡,这样相当于用下面盒子帮助实现了collapse的效果。

    <div class="form myanimate" ng-show="showFlag"></div>
    <div class="table" ng-class="{'mt':showFlag}" style="width: 500px;height: 300px;background-color: green;"></div>
    
    <style>
    .myanimate.ng-hide-add,
    .myanimate.ng-hide-remove {
    transition: all linear 0.5s;
    }
    .myanimate.ng-hide{
    opacity: 0;
    }
    
    .form{
       float: left;
    }
    .table{
       transition: all 0.5s linear;
       /*提高层级,以免被浮动遮住*/
       position: relative;
    }
    .mt{
       margin-top: 96px;
    }
    </style>

  2. 舍弃ng-animate的过渡效果,直接添加class类,当ng-hide状态时,让元素高度变为0,并且设置transition,简单粗暴有效。

    <div class="form" ng-show="showFlag" ng-class="{'end': !showFlag}">
    
    
    <style>
    .form{
       transition: all linear 0.5s;
       max-height: 96px; /*稍微大一点也没事*/
       overflow: hidden;
       opacity: 1;
    }
    .end{
       max-height: 0;
       opacity: 0;
    }
    </style>

猜你喜欢

转载自blog.csdn.net/longyin0528/article/details/81212137