不定高度展开收起动画 css/js 实现 您所在的位置:网站首页 vue做动画效果 不定高度展开收起动画 css/js 实现

不定高度展开收起动画 css/js 实现

#不定高度展开收起动画 css/js 实现| 来源: 网络整理| 查看: 265

不定高度展开收起动画

最近在做需求的时候,遇见了元素高度展开收起的动画需求,一开始是想到了使用 transition: all .3s; 来做动画效果,在固定高度的情况下,transition 动画很好使,满足了需求,但是如果要考虑之后可能还会有更改的情况下,如果每次都是用固定高度来做动画,会显得很繁琐,也很呆,就想到了使用 height: auto; 来做高度动画,但是,众所周知,高度设置成 auto 时是不会触发 transition 动画的

.container { height: 0; background-color: #ccc; overflow: hidden; transition: all .3s; } .container:hover { height: 1000px; }

效果如图,不能满足动画的要求

1.gif

在一番查找实验之后,目前发现了如下几种方法:

1. max-height 最大高度

transition 动画可以响应 max-height

.container { max-height: 0; background-color: #ccc; overflow: hidden; transition: all .3s; } .container:hover { max-height: 1000px; }

2.gif

但是使用 max-height 做动画有一个问题,如果设置的最大高度越大,但是实际高度确与最大高度相差甚远,那么整体的动画速度就会非常快,动画的时间只会是 实际高度 / 最大高度 * 动画时间,因为展开动画原本预期高度是设置的最大高度,所以整体时间是以最大高度完全展开所用时间来进行的,但是当到达实际高度的时候动画就停止了,所以最终动画时间会与期望时间相差甚远。

max-height 方法做动画也是一个好方法,如果能够确定大致高度的话,使用此方法是最简单也是最快的方法,但是如果不能确定大致高度或整体高度经常变化的话,可以考虑其他方法。

2. grid 动画

grid 网格布局,是一种较新的布局,号称是最强大的布局方案。grid 布局不是本文的介绍重点,并且较为复杂,如果感兴趣的话,可以参考相关文章,如:

写给自己看的display: grid布局教程 最强大的 CSS 布局 —— Grid 布局

grid 布局中可以使用 fr 单位,fr 单位是支持过度动画的(0fr=>1fr),将 grid 布局下的子元素,初始设置为0fr,在 :hover 状态下设置为 1fr,就能够实现不定高度动画效果,但是如果子元素有内容,在设置 0fr 的时候,会被其内容撑开,所以要给子元素添加 min-height: 0;

.container { display: grid; grid-template-rows: 0fr; overflow: hidden; transition: all .3s; } .container:hover { grid-template-rows: 1fr; } .container .child { min-height: 0; }

3.gif

如果想要实现带有基础高度的展开收起动画,我们可以设置 min-height: 100px;

.container .child { min-height: 100px; }

4.gif

虽然此时实现了带有基础高度的动画效果,但是可以看到,如果我把 transition: all 3s; 的动画时间设置的较大,就可以看出来,虽然有基础高度,但是整个动画的效果还是要实现 0fr 到 1fr 的动画效果,基础高度部分不会有动画效果,这也算是一个小的缺点,如果动画时间较短并且基础高度也不大的话,可以这样使用,并不会有太大的影响效果。

但是 grid 布局有可能有兼容性的问题,grid-template-rows 动画的支持可能有兼容性问题

7.png

3. clip-path

clip-path CSS 属性使用裁剪方式创建元素的可显示区域。区域内的部分显示,区域外的隐藏。(MDN 定义)

clip-path 是 css 中一个极其重要的属性,它的功能非常强大,能够切割各种形状。clip-path 的语法类似于 SVG 的语法,如果你熟悉 SVG 那么差不多就熟悉 clip-path 的写法,由于借鉴于 SVG 所以语法可能有些难度,由于本文重点不是介绍 clip-path 具体的可以自行搜索文章进行学习。

clip-path 在线生成工具

clip-path 的优势之一就是可以响应 css 动画,可以与 transtion 和 animation 产生交互,实现强大的动画效果,结合本文,先来实现一个展开动画效果

.container { background-color: #ccc; // clip-path 也会占位,使用 absolute 定位脱离文档流进行展开收起动画效果最好 position: absolute; overflow: hidden; transition: all 1s; clip-path: circle(15px at 60px 20px); } .container:hover { // circle 的大小与位置并不需要固定,只要确保最终展开能将所有可见区域显示即可 clip-path: circle(1000px at 60px 20px); } .container .child { color: #0ebeff; }

实现效果

10.gif

初始左上角的圆圈就是 clip-path: circle(15px at 60px 20px); 的效果,在鼠标 hover 时,transition 动画就可以响应 clip-path 的变化。最终实现的效果不同于其他例子中的直上直下的展开收起效果,但是个人感觉更加有意思也更加好看一些.

更改一下 clip-path 改成是矩形,效果和圆形差不多

.container { ... clip-path: polygon(10px 10px, 40px 10px, 40px 30px, 10px 30px); } .container:hover { clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); }

11.gif

我们再更改一下初始时矩形的状态,如果我们把矩形变成一个宽是父元素的宽,但是高是 0 的矩形,再将 :hover 的改到其他正常显示的元素上,如其父元素或兄弟元素上,最终效果如下图

.container { ... clip-path: polygon(0 0, 100% 0, 100% 0, 0 0); } .father:hover .container { // 如果使用 clip-path 设置了 position: absolute,记得父元素加上 relative position: relative; clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); }

12.gif

可以看到,我们最终实现了想要的效果,动画时间与效果我们可以自由控制,并且如果想要实现最开始的圆形展开也是完全没问题的。

13.png

上面是 clip-path 在 MDN 上的兼容性截图,clip-path 的兼容性不考虑 IE 已经可以了,但是显示 在 Safari 中动画不支持,我的电脑是 MacOS 13.3,Safari 的版本是 16.4,我自己试了一下,也可以支持,可能在 Safari 低版本中还有兼容性的问题(我也没有低版本的,所以没有办法测试),但是如果不考虑支持Safari 的话,完全可以使用此方案。

13.gif

4. js 控制动画

写这篇文章的原因是因为在看项目代码的时候看见了 $(.xx).slideDown() 方法实现了元素的下滑动画,觉得很不错,想学习一下怎么实现的,实现效果如下:

5.gif

但是在看元素的时候却只能看见下面的样子,发现不是 css 实现的,是使用 js 不断改变元素的高度来实现的:

6.gif

我又去看了一下 ant-design 的 Menu 组件,通过观察元素,发现其也是不断改变高度来实现的(Ps: 我并没有去看源码,如果有误,多谢指正)。

实现

首先要思考整个实现的思路

展开的时候,元素从无到有,我们应该首先获取整个元素的实际高度使用 offsetHeight 来获取,获取到整体高度后就要计算每一次增加或者减少的高度,通过定时器不断增加或减少元素的高度,直到到了最大高度或 0 后停止

展开

const element = document.getElementById('container'); let expandTimer = null; let offsetHeight = 0; // 获取元素总高度 element.style.display = 'block'; let height = 0; // 先将 display 设置为 block,获取到的 offsetHeight 才是正确的高度,之后才能设置元素高度 offsetHeight = element.offsetHeight; const stepHeight = offsetHeight / 30; element.style.height = height + 'px'; expandTimer = setInterval(() => { height += stepHeight; if (height >= offsetHeight) { clearInterval(expandTimer); element.style = null; return; } element.style.height = height + 'px'; }, 10);

收起

let collapseTimer = null; offsetHeight = element.offsetHeight; let height = offsetHeight; const stepHeight = offsetHeight / 30; element.style.height = height + 'px'; collapseTimer = setInterval(() => { height -= stepHeight; if (height { // 如果当前 expandTimer 值存在,就标识当前是正在展开或已经展开,接下来要进行的是收起操作 if (expandTimer) { clearInterval(expandTimer); expandTimer = null; // 收起时的初始高度是元素的当前实际高度,即使是元素在展开动画过程中,也要从当前元素高度进行收起动画 let height = element.offsetHeight; collapseTimer = setInterval(() => { height -= stepHeight; if (height { height += stepHeight; if (height >= offsetHeight) { // 当前高度如果已经到了元素的实际高度,就要清除定时器 clearInterval(expandTimer); // 将 expandTimer 设为 1 是因为当前是以 expandTimer 判断是否正在或已经进行了展开动画,所以要在完成时设为 1,在收起动画的开始时会将值设为 null expandTimer = 1; element.style = null; return; } element.style.height = height + 'px'; }, 10); } };

最终实现效果

9.gif

5. 总结

上面的四种方式实现效果都是各有千秋

max-height 方法实现是最简单,也是效率最高的方式,但是也有动画时间不定的缺陷 grid 方式实现比 max-height 稍微复杂一些,但是整体效果要比 max-height 更好,但是目前浏览器的支持方面可能有所不足,如果有低版本的兼容性要求的话,还是不能使用 clip-path 方式如果用户不太考虑苹果的话,是一个非常好的方法,而且可以做大更多效果的动画,还是得看苹果什么时候能够全面支持 js 方式整体最复杂,但是却没有上面两种方式的缺陷与问题,使用范围也更广泛,但是是 js 的实现方式,性能肯定是不如 css,虽然不如,但是由于整体操作也较为简单,所以也不会有什么性能问题

几种方法的取舍全看个人需求了。

如果有鼠标进入展开,离开收起的操作,可以配合使用 onmouseover onmouseout 事件来监听鼠标的进入离开。

其他还有像是 transform: scale(0); 的实现也是可以,但是整体动画效果就是一个缩小的效果,而且元素还会有占位问题,如果没什么要求也是可以使用的。

6. 引用 CSS 奇技淫巧:动态高度过渡动画 原生JavaScript实现jQuery中的slideUp和slideDown滑动效果 CSS 如何让auto height完美支持过渡动画?


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

      专题文章
        CopyRight 2018-2019 实验室设备网 版权所有