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

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

不定高度展开收起动画

最近在做需求的时候,遇见了元素高度展开收起的动画需求,一开始是想到了使用 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; 的动画时间设置的较大,就可以看出来,虽然有基础高度,但是整个动画的效果还是要实现 0fr1fr 的动画效果,基础高度部分不会有动画效果,这也算是一个小的缺点,如果动画时间较短并且基础高度也不大的话,可以这样使用,并不会有太大的影响效果。

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

3. js 控制动画

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

5.gif

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

6.gif

我又去看了一下 ant-designMenu 组件,通过观察元素,发现其也是不断改变高度来实现的(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 <= 0) {clearInterval(collapseTimer);element.style = null;return;}element.style.height = height + 'px';
}, 10);

8.gif

现在能够正确展开收起,但是我们在展开收起的时候也会有相反的操作,比如鼠标进入元素展开离开收起,在展开的过程中鼠标离开了,我们应该立刻就将元素收起,而不是等动画结束后在进行下一个动画,所以要将展开收起操作合并操作才可以

const element = document.getElementById('container');
let expandTimer = null;
let collapseTimer = null;
// 我认为在一次展开后,直到收起完成之前,这个元素的实际高度都不应该发生变化,但是可以在下一次展开时发生变化,所以在展开时会进行赋值,在收起完成时会将此值清空
let offsetHeight = 0;
let stepHeight = 0;const handleClick = () => {// 如果当前 expandTimer 值存在,就标识当前是正在展开或已经展开,接下来要进行的是收起操作if (expandTimer) {clearInterval(expandTimer);expandTimer = null;// 收起时的初始高度是元素的当前实际高度,即使是元素在展开动画过程中,也要从当前元素高度进行收起动画let height = element.offsetHeight;collapseTimer = setInterval(() => {height -= stepHeight;if (height <= 0) {// 高度小于等于 0 代表动画完成,将数据进行重置clearInterval(collapseTimer);offsetHeight = 0;// 要将元素的高度置为 null,不然会影响下一次展开时获取正确的高度element.style.height = null;// display 设为 null,要将元素隐藏element.style.display = 'none';return;}element.style.height = height + 'px';}, 10);} else {clearInterval(collapseTimer);collapseTimer = null;// 获取元素总高度element.style.display = 'block';let height = 0;如果当前没有 offsetHeight 就要重新获取if (!offsetHeight) {offsetHeight = element.offsetHeight;// 每一次给元素添加或减少的高度,除以 30 是自己设定的,跟下面定时器的每次间隔时间一起控制整个高度动画的时长,也可以给函数添加第二个时间参数,可以自由控制动画时间stepHeight = offsetHeight / 30;} else {// 如果有 offsetHeight 就代表正在进行收起动画,应该从收起动画的当前高度进行展开动画height = element.offsetHeight;}element.style.height = height + 'px';expandTimer = setInterval(() => {height += stepHeight;if (height >= offsetHeight) {// 当前高度如果已经到了元素的实际高度,就要清除定时器clearInterval(expandTimer);// 将 expandTimer 设为 1 是因为当前是以 expandTimer 判断是否正在或已经进行了展开动画,所以要在完成是设为 1,在收起动画的开始时会将值设为 nullexpandTimer = 1;element.style = null;return;}element.style.height = height + 'px';}, 10);}
};

最终实现效果

9.gif

4. 总结

上面的三种方式实现效果都是各有千秋 - max-height 方法实现是最简单,也是效率最高的方式,但是也有动画时间不定的缺陷 - grid 方式实现比 max-height 稍微复杂一些,但是整体效果要比 max-height 更好,但是目前浏览器的支持方面可能有所不足,如果有低版本的兼容性要求的话,还是不能使用 - js 方式整体最复杂,但是却没有上面两种方式的缺陷与问题,使用范围也更广泛,但是是 js 的实现方式,性能肯定是不如 css,虽然不如,但是由于整体操作也较为简单,所以也不会有什么性能问题

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

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

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

本文转载于:

https://juejin.cn/post/7249536369474486329

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/1006.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【嵌入式模型转换】2. 算能盒子SE5 芯片板子BM1684 sophon-pipeline

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言1. 开始安装 前言 文章1&#xff0c;我们在SE5上实现了&#xff0c;SOC模式下的 C 和 python-sail的模型转换&#xff0c;文章连接&#xff1a; 【嵌入式模型转…

微服务实战项目-学成在线-课程发布模块

学成在线-课程发布模块 1 模块需求分析 1.1 模块介绍 课程信息编辑完毕即可发布课程&#xff0c;发布课程相当于一个确认操作&#xff0c;课程发布后学习者在网站可以搜索到课程&#xff0c;然后查看课程的详细信息&#xff0c;进一步选课、支付、在线学习。 下边是课程编辑…

观察者模式(二十)

相信自己&#xff0c;请一定要相信自己 上一章简单介绍了迭代器模式(十九), 如果没有看过, 请观看上一章 一. 观察者模式 引用 菜鸟教程里面 观察者模式介绍: https://www.runoob.com/design-pattern/observer-pattern.html 当对象间存在一对多关系时&#xff0c;则使用观察…

【MySQL】MySQL中SQL执行流程

文章目录 一、MySQL语句执行流程1.1、主要的原因有以下几点1.2、具体执行流程图如下 二、存储引擎三、MySQL的架构与内部模块四、崩溃恢复时如何判断事务是否需要提交 一、MySQL语句执行流程 连接器&#xff08;Connector&#xff09;&#xff1a; 当客户端发送一个连接请求时…

矩阵通引入AIGC,帮助企业挖掘用户线索

据CNNIC报告数据&#xff0c;截止2022年6月&#xff0c;我国网民规模达10.51亿&#xff0c;其中短视频用户规模为9.62亿&#xff0c;网络直播用户规模达7.16亿&#xff0c;占网民整体的68.1%。 庞大的用户体量为直播带货奠定了基础&#xff0c;给品牌商家提供了新流量阵地&…

自动刷新工具--可以自动编辑安居客房源信息

本工具可以自动刷新安居客的房源信息&#xff0c;不是爬虫&#xff0c;就是一款解放劳动力的RPA工具 使用方法&#xff1a; 1. 首先输入要自动刷新的房源id 2.点击 开始执行 如果需要免密登陆&#xff0c;需要在个人中心填上anjuke的账密 定时执行 声明&#xff1a;此工具只是…

我是怎么把win11一步一步变成Mac的

目录 【三指拖动】 【空格预览】 【切换Ctrl和Alt】 【使用Linux命令】 【其它】 之前很长一段时间在MacBook上面开发习惯了&#xff0c;然后因为一些原因现在换到了windows上面&#xff0c;不管是使用上还是系统上都很不习惯&#xff0c;因此做了一些改造&#xff0c;…

【Linux】应用层协议:HTTP和HTTPS

每个人都可以很喜欢每个人&#xff0c;但喜欢治不了病&#xff0c;喜欢买不了东西&#xff0c;喜欢不能当饭吃&#xff0c;喜欢很廉价… 文章目录 一、HTTP协议1.URL1.1 URL的组成1.2 urlencode && urldecode 2.HTTP协议格式2.1 http请求和响应的格式2.2 通过代码来进行…

Zookeeper集群的特点

一、Zookeeper集群的特点 Zookeeper:一个领导者 (Leader)&#xff0c;多个跟随者 (Follower) 组成的集群集群中只要有半数以上节点存活&#xff0c;Zookeeper集群就能正常服务。所以Zookeeper适合安装奇数台服务器全局数据一致:每个Server保存一份相同的数据副本&#xff0c;C…

状态模式(State)

定义 状态是一种行为设计模式&#xff0c;让你能在一个对象的内部状态变化时改变其行为&#xff0c;使其看上去就像改变了自身所属的类一样。 前言 1. 问题 状态模式与有限状态机的概念紧密相关。 其主要思想是程序在任意时刻仅可处于几种有限的状态中。在任何一个特定状态…

Transformer中的Q,K,V

Query&#xff0c;Key&#xff0c;Value的概念取自于信息检索系统&#xff0c;举个简单的搜索的例子来说。当你在某电商平台搜索某件商品&#xff08;年轻女士冬季穿的红色薄款羽绒服&#xff09;时&#xff0c;你在搜索引擎上输入的内容便是Query&#xff0c;然后搜索引擎根据…

大数据分析的Python实战指南:数据处理、可视化与机器学习【上进小菜猪大数据】

上进小菜猪&#xff0c;沈工大软件工程专业&#xff0c;爱好敲代码&#xff0c;持续输出干货。 引言&#xff1a; 大数据分析是当今互联网时代的核心技术之一。通过有效地处理和分析大量的数据&#xff0c;企业可以从中获得有价值的洞察&#xff0c;以做出更明智的决策。本文将…