惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Application and Cybersecurity Blog
Application and Cybersecurity Blog
G
GRAHAM CLULEY
P
Privacy International News Feed
L
LINUX DO - 热门话题
C
Cisco Blogs
T
Tor Project blog
AWS News Blog
AWS News Blog
K
Kaspersky official blog
C
Cybersecurity and Infrastructure Security Agency CISA
Cyberwarzone
Cyberwarzone
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
CERT Recently Published Vulnerability Notes
P
Proofpoint News Feed
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
Lohrmann on Cybersecurity
C
CXSECURITY Database RSS Feed - CXSecurity.com
Project Zero
Project Zero
Attack and Defense Labs
Attack and Defense Labs
Latest news
Latest news
The Last Watchdog
The Last Watchdog
Apple Machine Learning Research
Apple Machine Learning Research
Simon Willison's Weblog
Simon Willison's Weblog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Cloudbric
Cloudbric
Last Week in AI
Last Week in AI
S
Security Affairs
Google DeepMind News
Google DeepMind News
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
有赞技术团队
有赞技术团队
V
Visual Studio Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
小众软件
小众软件
P
Palo Alto Networks Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
S
Security @ Cisco Blogs
V
V2EX
S
Secure Thoughts
人人都是产品经理
人人都是产品经理
月光博客
月光博客
博客园_首页
T
Troy Hunt's Blog
爱范儿
爱范儿
N
News and Events Feed by Topic
Hugging Face - Blog
Hugging Face - Blog
雷峰网
雷峰网
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
P
Privacy & Cybersecurity Law Blog
V
Vulnerabilities – Threatpost

Mereith's Blog

记一次代理转发问题的排查与解决 PVE DataCenter Manager 端口号跳转错误修复 k8s + jenkins + gitlab 触发器 【转载】没有编程生产力这样的东西 【后续】居然被 ddos 了 居然被 ddos 了 Tagger - 让版本标签管理更简单 openclash 开启后端口转发失效 nextjs 启动时执行代码 买了太多VPS不知道干什么,干脆做个网站 juhost Level-1 测评 VMISS CN - Hong Kong VMISS JP - Tokyo - BGP 测评 VMISS CN - Hong Kong - BGP HomeLab 的终点是 最近在做的事情 因为选择艰难症,自己写了一套开源博客系统 手撸一个nodejs分布式爬虫,还要可视化 HomeLab 分享 我的个人工作流——开源项目推荐 动手写一个超简单的编译器 safari 插件开发 Safari Extensions Preferences 按钮无响应 设置移动端软键盘回车按钮文案 nginx proxy manager 非标准端口反代 Host 不对 监控 k8s ingress 自动添加域名 DNS 解析 使用 ingress 注解给 traefik ingress 添加中间件 使用流水线功能为文章添加固定结尾 为 VanBlog 添加一个小挂件 基于React/umi/egg自建博客系统 如何正确停止 NodeJS 子进程 k8s pod 替换策略 让威联通、pve 共享 ups 实现断电关机 Ubuntu 配置 samba 混合云部署k3s集群(基于wireguard) pve 无法启动 grub lvmid not found error antd Password 关闭自动补全 code-server node not found pve no quorum 错误 || 无法登录 pve Cannot Initiate CMAP Service pve 节点删不干净 PVE 指针(鼠标)漂移 pve 重启网络 Proxmox VE 直通显卡 ProxmoxVE (PVE) 7.0 换源升级 用 SOCAT 端口转发 git 无法 pull 仓库refusing to merge unrelated histories 用 vuepress 搭建私人知识库 js 防抖节流中的 this 与箭头函数 Dockerfile 给 Ubuntu 换源 docker 内访问宿主机 docker 删除无用镜像 nginx 301 问题 Node.js 流式编程 关于我重写了三次博客项目这件事 css flex 布局超出隐藏 css 隐藏滚动条 css 文字超出隐藏并显示省略号 css 填充 svg 颜色 css 黑白滤镜 nginx 反代丢端口 docker-compose 部署 matomo 分析系统 基于 strapi 开发应用 strapi 关闭 Content Security Policy nginx 反代 rewite url ssh 通过跳板机访问目标机器 Cent os 7 安装 zsh 5.6 以上版本 win11 任务栏不合并 bash 数组操作 vim 修改文件格式为 unix k8s 集群 control-plane-endpoint 主机名 git https 保存密码 Bash 判断命令存在 & 重定向 python 导出项目依赖 cent os 7 安装 gcc 版本 9 System limit for number of file watchers reached cent os 7 安装 git 2.x css 下划线中间向两边滑动效果 linux 挂载 SMB/CIFS linux 清除 ACL windows 命令行安装 inf 驱动 css 文字不可选择 iperf3 网络测速工具 cent os 换源 k8s 1.21.3 从 docker 迁移到 containerd 解决Nginx安装错误:No package nginx available 问题 从pve的硬盘里导出数据 Errors were encountered while processing adguardhome配合clash pve中使用LXC容器安装OMV LXC直通硬盘 nginx反代模版 docker常用开启容器的命令 PVE的LXC容器中安装Docker ProxmoxVE系统分区相关 基于clash搭建旁路网关 Portainer添加节点 linux声卡设置及录音音 OpenWrt用VLAN PVE换源及去掉订阅提示和改端口 adguardhome踩坑
css 实现展开收起动画
wanglu@mereith.com (mereith) · 2022-07-26 · via Mereith's Blog

请注意,本文编写于 1412 天前,最后修改于 1411 天前,其中某些信息可能已经过时。

参考: CSS 实现展开动画

展开收起效果是比较常见的一种交互方式,通常的做法是控制 display 属性值在 none 和其它值之间切换,虽说功能可以实现,但是效果略显生硬,所以会有这样的需求——希望元素展开收起能具有平滑的效果。

max-height

首先想到的是通过 height 在 0 与 auto 之间切换,但是结果可能并不会是我们所预期的那样,原因是我们将要展开的元素内容是动态的,即高度值不确定,因此 height 使用的值是默认的 auto,从 0px 到 auto 是无法计算的,因此无法形成过渡或动画效果。

据此我们可以使用 max-height,将 max-height 从 0 过渡到一个能够大于完全显示内部元素的值,展开后的 max-height 值,只需要设定为保证比展开内容高度大的值即可,在 max-height 值比 height 值大的情况下,元素仍会默认采用自身的高度值即 auto,如此一来一个高度不定的元素展开收起动画效果就实现了。

请注意这种方式实现还是有限制的,使用 CSS 进行过渡动画的时候依旧是通过计算 0 到设定的 max-height 高度值进行计算的,在实际应用中如果 max-height 值太大,在元素收起的时候将会产生延迟的效果,这是因为在收起时,max-height 从设定的特别大的值,到元素自身高度值的变化过程将占用较多时间,此时画面表现则会产生延迟的效果。因此建议将 max-height 值设置为足够安全的最小值,这样在收起时即使有略微延迟,也会因为时间很短,难以被用户感知,将不会影响体验。

<!DOCTYPEhtml> <html> <head> <title>展开动画</title> <styletype="text/css"> .page { width: 200px; padding: 10px20px; border: 1pxsolid#eee; } .container { overflow: hidden; } .container > .options { transition: all0.5s; max-height: 0; } .container > .unfold { max-height: 150px; } .container > .btn { color: #4c98f7; cursor: pointer; text-decoration: underline; } </style> </head> <body> <divclass="page"> <divclass="container"> <divclass="btn"onclick="operate(this)"unfold="1">展开</div> <divclass="options"> <divclass="option">选项1</div> <divclass="option">选项2</div> <divclass="option">选项3</div> <divclass="option">选项4</div> <divclass="option">选项5</div> <divclass="option">选项6</div> <divclass="option">选项7</div> </div> </div> </div> </body> <scripttype="text/javascript"> functionoperate(btn) { constoptionsNode = document.querySelector(".container > .options"); constunfold = btn.getAttribute("unfold"); if (unfold && unfold === "1") { btn.innerText = "收缩"; optionsNode.classList.add("unfold"); } else { btn.innerText = "展开"; optionsNode.classList.remove("unfold"); } btn.setAttribute("unfold", unfold === "0" ? "1" : "0"); } </script> </html>

height

使用 max-height 必定有一定的局限性,那么不如我们在 DOM 加载完成后就取得元素的实际高度并保存,之后直接利用这个真实高度与 0 进行动画过渡即可,因为浏览器的渲染顺序,在解析 JavaScript 时会阻塞 DOM 的渲染,所以在获取元素实际高度再设置高度为 0 的过程中一般不会出现闪烁的情况,如果实在担心因为获取高度之后再将高度设置为 0 可能会有一个闪烁的过程,那么我们可以取得元素父节点后调用 cloneNode(true)方法或者 innerHTML 方法取得字符串再 innerHTML 到一个新创建的节点,目的就是将其拷贝,之后将其使用绝对定位等放置到屏幕外即将其设置到屏幕能够显示的外部区域,注意此时要设置 body 的 overflow: hidden;,之后利用 getComputedStyle 取得实际高度,然后再将其移出 DOM 结构,此时有了实际高度就可以进行动画过渡了,下面简单的实现一下在 DOM 加载时便取得实际高度进行动画实现。

<!DOCTYPE html> <html> <head> <title>展开动画</title> <style type="text/css"> .page { width: 200px; padding: 10px 20px; border: 1px solid #eee; } .container { overflow: hidden; } .container > .options { transition: all 0.5s; } .container > .btn { color: #4c98f7; cursor: pointer; text-decoration: underline; } </style> </head> <body> <div class="page"> <div class="container"> <div class="btn" onclick="operate(this)" unfold="1">展开</div> <div class="options"> <div class="option">选项1</div> <div class="option">选项2</div> <div class="option">选项3</div> <div class="option">选项4</div> <div class="option">选项5</div> <div class="option">选项6</div> <div class="option">选项7</div> </div> </div> </div> </body> <script type="text/javascript"> (function init(win, doc) { const optionsNode = document.querySelector(".container > .options"); optionsNode.setAttribute( "real-height", win.getComputedStyle(optionsNode).height ); optionsNode.style.height = "0px"; })(window, document); function operate(btn) { const optionsNode = document.querySelector(".container > .options"); const unfold = btn.getAttribute("unfold"); const realHeight = optionsNode.getAttribute("real-height"); if (unfold && unfold === "1") { btn.innerText = "收缩"; optionsNode.style.height = realHeight; } else { btn.innerText = "展开"; optionsNode.style.height = "0px"; } btn.setAttribute("unfold", unfold === "0" ? "1" : "0"); } </script> </html>

translateY

还有一种常用实现动画的方式,即首先将外层元素没有动画过渡的形式直接展开,再将选项加入动画缓慢下落,通常利用 transform: translateY();去实现这个缓慢下降的动画,在微信的 WEUI 小程序组件库的首页就是采用这种实现方式。

<!DOCTYPE html> <html> <head> <title>展开动画</title> <style type="text/css"> .page { width: 200px; padding: 10px 20px; border: 1px solid #eee; } .container, .options-container { overflow: hidden; } .options-container { height: 0; } .container .options { transition: all 0.5s; transform: translateY(-100%); } .container .unfold { transform: translateY(0); } .container > .btn { color: #4c98f7; cursor: pointer; text-decoration: underline; } </style> </head> <body> <div class="page"> <div class="container"> <div class="btn" onclick="operate(this)" unfold="1">展开</div> <div class="options-container"> <div class="options"> <div class="option">选项1</div> <div class="option">选项2</div> <div class="option">选项3</div> <div class="option">选项4</div> <div class="option">选项5</div> <div class="option">选项6</div> <div class="option">选项7</div> </div> </div> </div> </div> </body> <script type="text/javascript"> function operate(btn) { const optionsNode = document.querySelector(".container .options"); const optionsContainer = document.querySelector(".options-container"); const unfold = btn.getAttribute("unfold"); if (unfold && unfold === "1") { btn.innerText = "收缩"; optionsNode.classList.add("unfold"); optionsContainer.style.height = "auto"; } else { btn.innerText = "展开"; optionsNode.classList.remove("unfold"); optionsContainer.style.height = "0px"; } btn.setAttribute("unfold", unfold === "0" ? "1" : "0"); } </script> </html>