blog
blog copied to clipboard
带突出三角形的下拉对话框动画实现
在很多时候需要实现一个弹出框,效果类似这样:
要留意的是这个对话框有一个指示弹出位置的三角,并且需要实现下拉动画和上滑动画。
一个 box 加 :before
针对需求,有初步的方案
- 指示三角:在 box 前加伪类
:before
、三角形通过边框法实现border-bottom
- 动画:
transition height
由于 box 高度可以固定,直接使用height
做动画
关于边框法实现三角形可以自行 Google,原理很简单,就是把 box model 的内容去掉 width: 0; height: 0
,利用四个 border
都是三角形设置三条边为透明即可。
这里要注意的是三角形的
top
应该是两倍的上下边框的border-width
这里我们发现这个三角形没有出现,原因是我们在 box 上加了 overflow: hidden
,这是为了让 box 的 height
变成 0 时,box 的内容不会溢出到 box 外(不然这个下拉动画就没用了)。由于要使用 transition height
就必须加上这个 overflow: hidden
,那么所有要在 box 的 HTML 内部实现三角形的方法都不会 work,因为这个三角形实际上一定会 overflow 到 box 外。
分成两个 box
既然不能在 box 内部放这个三角形,那就放到外面呗,在 box 的同层加一层 div,额外放这个三角形。
具体实现时,把这个 before-box
的背景设成透明就 ok 了。
两个 box 后的 transition
分成两部分之后面临的第二个问题是,动画怎么办?当我还是一个 box 的时候,设置了 transition
,js 要做的事就很简单:
// 下拉
ele.style.height = '200px'
// 收起
ele.style.height = '0px'
但分成两部分之后,如果简单得认为只需要在 before-box 上也设一个 transition height
然后这样:
// 下拉
before.style.height = '20px'
ele.style.height = '200px'
// 收起
before.style.height = '0px'
ele.style.height = '0px'
那就 TOO NAIVE 了,这时候你会发现这两个 div 是同步进行动画的,给人一种强烈的违和感。
既然我想有先后顺序,第一个想到的应该是 transition-delay
,加个延迟就好了啊:
.before-box {
transition: height 0.05s ease-out;
}
.box {
transition: height 0.3s 0.05s ease-out;
}
下拉的时候很完美啊,但收起的时候就悲剧了,before-box 先收起,然后才是 box 的动画,违和感更强了。
这时我们知道,其实我们需要一种带方向性的 transition
机制,从 0 到 100px 和从 100px 到 0 我们希望能够 apply 不同的 transition 参数(这里是不同的 delay)。
于是找了一下,貌似 transition
并不支持这种方向性的机制。但是没关系,我们可以通过 js 自行创造,用不同的 class 来预定义不同的 transition 参数,然后在实际动画前变更这些 class 就可以实现带方向的 transition
了。
.triangle-show {
transition: height 0.05s linear;
}
.triangle-hide {
transition: height 0.05s 0.35s linear;
}
.box-show {
transition: height 0.3s 0.05s ease-out;
}
.box-hide {
transition: height 0.3s ease-out;
}
在 show/hide 之前切换 class
// 下拉
before.classList.add('triangle-show')
box.classList.add('box-show')
before.classList.remove('triangle-hide')
box.classList.remove('box-hide')
...
// 收起
before.classList.add('triangle-hide')
box.classList.add('box-hide')
before.classList.remove('triangle-show')
box.classList.remove('box-show')
当然如果不想定义这么多的 class 也可以直接选择在 js 中更改 transition 的属性
ele.style.transitionDelay = '0.35s'