记录弹框动画实现的几种方案

时间:2021-2-20 作者:admin

你需要了解的知识:

  • display:none无法应用transition效果,甚至还会有破坏作用 因为 浏览器渲染dom时,如果某个节点是 display: none 的,那么就不会在渲染树中显示,而且display:none会引发回流,改变布局,自然也就不会有过渡过程了
  • opacity化的元素配合transition依然可以点击触发事件,hover效果等,仅仅用opcityl来过渡动画是不行的, 有人说可以结合visibility:visibile/hidden ,但是这中隐藏的元素是会占位的,此种方法也不可取

那我们隐藏节点的时候怎么添加动画呢?

我们以一个弹框显示隐藏的案例,来看以下几种方案的优劣。【以下方案都是伪代码,只讨论实现方式】

方案1:使用vue的transiton内置动画配合v-if/v-show来控制显示隐藏【推荐】

vue会相应地插入或删除 dom元素

方案2:opacity配合 scale3d来控制弹框显隐**

// 动画过渡
.dialogMask{
      transition: all .3s ease-in;
}
//弹框隐藏
.hide{
      opacity: 0;
      transform: scale3d(1, 1, 0);
}
//弹框显示
.show{
     opacity: 1;
     transform: scale3d(1, 1, 1);
}

opacity控制过渡动画
scale Z轴隐藏时变为0是为了删除dialogMask内部的节点。这样当弹框隐藏时就只有一个外部的节点,可以减少初始时渲染的开销

注意点:假如隐藏时让内部内容立马隐藏,外部遮罩慢慢隐藏,可以给内部内容加wx:if=”{{show}}”

此方法适用于原生js或者小程序

方案3:display配合setTimeout

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>显示隐藏动画</title>
    <style>
        .model {
            width: 100px;
            height: 100px;
            background: #787878;
            margin: 0 auto;
            transition: all .3s;
        }
    </style>
</head>

<body>
    <button onclick='dianji()'>点击</button>
    <div class="model">
        内容
    </div>
    <script>
        let model = document.querySelector('.model')
        function dianji() {
            if (model.style.display === 'none') {
                model.style.display = 'block'
                // 宏任务特性
                setTimeout(() => {
                    model.style.opacity = 1
                }, 0)
            } else {
                model.style.opacity = 0
                setTimeout(() => {
                    model.style.display = 'none'
                }, 300);
            }
        }

    </script>
</body>
</html>

注意:显示时加动画是利用宏任务的特性setTimeout(()=>{},0)

隐藏时setTimeout需要设置和transition过渡的时常一样。当然你也可以监听transitionend /animationend动画结束事件,当结束时隐藏或者移除元素

方案4:添加移除dom时添加动画

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>删除移除元素时添加动画</title>
</head>
<style>
    #app {
        width: 100px;
        height: 100px;
        transition: all 300ms cubic-bezier(0.23, 1, 0.32, 1);
        transform-origin: center top;
        border: 1px solid sienna;
        text-align: center;
        line-height: 100px;
        margin-top: 15px;
    }

    .show {
        opacity: 1;
        transform: scaleY(1);
    }

    .hide {
        opacity: 0;
        transform: scaleY(0);
    }
</style>

<body>
    <button id="btn">点击</button>
    <div id="app" class="show">
        内容
    </div>
    <script>
        let element = document.getElementById("app");
        let btn = document.getElementById('btn')
        let copy = null
        function transition(ele, fn) {
            console.log(1)
            ele.addEventListener("transitionend", () => {
                if (document.body.contains(ele)) {
                    document.body.removeChild(ele)
                    fn()
                }
            }, false);
        }
        btn.addEventListener('click', () => {
            if (!document.body.contains(element)) {
                let ele = document.body.appendChild(copy)
                setTimeout(() => {
                    ele.classList.replace("hide", "show");
                    element = ele
                }, 0)
            } else {
                element.classList.replace("show", "hide");
                copy = element.cloneNode(true); //深克隆
                transition(element, () => {
                    element = null
                })
            }
        })
    </script>
</body>

</html>

预览

codepen.io/wensiyuanse…

方案5:用动画库来处理过渡动画

    //startMove是封装好的动画库
      let model = document.querySelector('.model')
       function dianji() {
           if (model.style.display === 'none') {
               model.style.display = 'block'
               startMove(model, {
                   opacity: 100
               })
           } else {
               startMove(model, {
                   opacity: 0
               }, () => {
                   model.style.display = 'none'
               })
           }
       }

movejs

function startMove(obj, json, fn) {
    clearInterval(obj.iTimer);
    var iCur = 0;
    var iSpeed = 0;

    obj.iTimer = setInterval(function () {

        var iBtn = true;

        for (var attr in json) {

            var iTarget = json[attr];

            if (attr == 'opacity') {
                iCur = Math.round(css(obj, 'opacity') * 100);
            } else {
                iCur = parseInt(css(obj, attr));
            }

            iSpeed = (iTarget - iCur) / 8;
            iSpeed = iSpeed > 0 ? Math.ceil(iSpeed) : Math.floor(iSpeed);

            if (iCur != iTarget) {
                iBtn = false;
                if (attr == 'opacity') {
                    obj.style.opacity = (iCur + iSpeed) / 100;
                    obj.style.filter = 'alpha(opacity=' + (iCur + iSpeed) + ')';
                } else {
                    obj.style[attr] = iCur + iSpeed + 'px';
                }
            }

        }

        if (iBtn) {
            clearInterval(obj.iTimer);
            fn && fn.call(obj);
        }

    }, 30);
}

function css(obj, attr) {
    // 兼容ie
    if (obj.currentStyle) {
        return obj.currentStyle[attr];
    } else {
        // 第二个参数表示的是:after、:before之类的伪类 如果不用伪类的话设置为null即可
        return getComputedStyle(obj, false)[attr];
    }
}

参考文章:

www.zhangxinxu.com/wordpress/2…

segmentfault.com/a/119000001…

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。