09月14, 2017

记一个弹窗相关的css bug

去年写了一个modal,然后在整个部门都在用,虽然未来的发展是整个要变成react,但目前还是得沿用,所以在使用的过程中,难免会发现一些问题。

譬如垂直居中的问题:

position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);

看似没有问题,但其实还是有问题,主要是以下两个问题:

  • 在计算拖动位置的时候,最左边不是判断<0,而是判断小于弹层的一半,还有其他几个方向,也要做相应的处理(这个解决不是难事)
  • 自适应的弹窗,当宽度或者高度不是偶数时,字体会模糊。(暂时无解)

也许有人会问,为什么弹窗出来时,设定它的left、top值呢?

因为弹窗里面可能会有按钮来撑开弹窗本身的高度,一旦一开始设定它的top值,那么整个布局会往下撑开,而不再是垂直居中了。

所以想了想,决定用flex来布局,代码如下:

<div class="dui-dialog-wrap">
    <div class="dui-dialog"></div>
</div>
.dui-dialog-wrap {
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    touch-action: cross-slide-y pinch-zoom double-tap-zoom;
    text-align: center;
    overflow: hidden;
    background: rgba(0,0,0,.6);
    display: -webkit-flex;
    display:         flex;
    -webkit-align-items: center;
    align-items: center;
    -webkit-justify-content: center;
    justify-content: center;
    &:before {
        content: "";
        display: inline-block;
        vertical-align: middle;
        height: 100%
    }
}

当只有一层dui-dialog时,是完全OK的。但是当结构如下时:

<div class="dui-dialog-wrap">
    <div class="dui-dialog"></div>
    <div class="dui-dialog"></div>
</div>

两个div就会水平靠在一起。但是我们想要的效果是第二个div在第一个div上面啊。

于是就有了下面的css:

.dui-dialog + .dui-dialog {
    position: absolute;
}

在chrome58下完美通过,就开心地发了一个版本。

然而事情没我想的这么简单,今天同事报了一个bug给我,说在微端(可以理解成用webkit内核的客户端)下弹窗错位。

我想到可能是webkit内核太老了,据说是41左右,当好虚拟机里面有一个chrome48的版本,所以我就打开测试,截图如下:

alt

看到之后,我的内心是崩溃的。

好在查阅了一些资料,看到张鑫旭写的小tip: margin:auto实现绝对定位元素的水平垂直居中,有所明悟。

将css改为如下代码:

.dui-dialog + .dui-dialog {
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    margin: auto;
}

截图如下:

alt

发现height: auto失效了,得赋一个具体的值才能生效,这显然也不是我所想要的效果。

绕了一圈还是回来了,还是得想办法解决transform字体模糊的问题。

在大漠的文章中,有提及解决字体模糊的方式:

/* 在父类容器中添加 */
-webkit-transform-style: preserve-3d; 
-moz-transform-style: preserve-3d; 
transform-style: preserve-3d;

但实际测试发现并不行,中途闪过一个灵感,在完成初始化之后判断height为auto,得到它真实的height值,然后看它是否能被2整除,不行的话,+1再写回去。代码大概如下:

handleDialogHeight() {
    var height = this.dialogDom.style.height;
    if (height == "auto") {
        height = document.defaultView.getComputedStyle(this.dialogDom, false)["height"];
        height = parseInt(height);
        if(height % 2 != 0) {
            console.log("不能被2整除");
            this.dialogDom.style.height = height + 1 + "px";
        }
    }
}

但是一旦弹窗里面有内容要撑开弹窗,那上面的方案就完全不行了,外层容器只能是height: auto

看来这个问题是无解了,怪只怪自己当初设计API时欠考虑,应该是提供两个方法:

  • setTop(top)
  • adjustPos(): 在弹窗里面内容触发事件将弹窗向下撑开时,调用此方法来重新归位元素top值

本文链接:www.my-fe.pub/post/modal-css-bug.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。