简单且实用的CSS小技巧——手风琴动画(一)

如何实现手风琴动画?

如果让你用CSS实现一个手风琴时的动画效果,例如:

你会怎样实现?我的第一想法是:这还不简单,用 transition: height 不就能轻易实现了吗?给隐藏的内容设置初始高度 height : 0 ,当其展开时再动态给 height 赋值,height 变化时应该就可以触发 transition 的过渡动画了。

根据这个思路,以下是用 vue 编写的示例代码:

js 复制代码
<template>
    <button @click="toggle">button</button>
    <div id="expandable" :class="show? 'show': 'hidden'">
      <div>Qui tempor Lorem cillum do ex nisi voluptate consequat cupidatat.</div>
    </div>
    <div>Cillum proident Lorem Lorem nisi aliqua magna Lorem labore laboris mollit.</div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const show = ref(false);

function toggle() {
  show.value = !show.value;
}

</script>

<style scoped>
#expandable {
  overflow: hidden;
  transition: height 1s ease;
}

#expandable.show {
  height: 100%;
}

#expandable.hidden {
  height: 0;
}

</style>

让我们看看效果:

嗯?怎么和我想得不一样,我动画呢?

动画未生效的原因是 transition 的机制:

Transitions enable you to define the transition between two states of an element.

简单来说,Transition 动画只能在两个确定值之间触发,在上面的例子中,height : 0 是一个确定值,但 height : 100% 可不是,如果我们将 height : 100% 换成如 height : 20px ,则动画就可以正常生效了:

20px 只是一个我随机指定的值,要获得准确的高度,还需要使用 Js 来搜索隐藏元素,获取其真实高度,并动态设置其 height 属性。因为这不是本文的主题,此处不再详述。

那么,只使用 Css 要如何实现这个效果呢?以下的这串简单的 css 代码可以神奇的帮我们实现这个功能:

css 复制代码
#expandable {
  display: grid;
  overflow: hidden;
  transition: grid-template-rows 1s ease;
}

#expandable.show {
  grid-template-rows: 1fr;
}

#expandable.hidden {
  grid-template-rows: 0fr;
}

#expandable div {
  min-height: 0;
}

好的。本文到此结束,感谢您的阅读,我们下期再见。

...

:D,开玩笑的, grid-template-rows: 0fr 是啥意思? 为啥要使用 min-height:0 而不是 height : 0 ? 我们慢慢来解释。

grid-template-rows:0fr

学习的最好办法是从熟悉的东西入手,因此,讨论 0fr 可以从 1fr 开始,讨论 grid-template-rows 可以从平时用的更多的 grid-template-columns 入手。

fr 的意思是 「fraction 分数、部分」,grid-template-columns: 1fr 表示将剩余的空间分为 1 份,每个元素占据 1 份(全部)的空间:

js 复制代码
<template>
    <div id="wrapper">
        <div id="inner">
            Lorem labore excepteur eiusmod anim.
        </div>
    </div>
</template>
<style scoped>
#wrapper {
    display: grid;
    grid-template-columns: 1fr;
    border: 1px solid #000;
}
</style>

而如果 fr 的值小于 1,如 0.5fr ,则表示将剩余的空间分为 1 份,每个元素占据 0.5 份空间,剩余空间留白。

css 复制代码
<style scoped>
#wrapper {
    display: grid;
    grid-template-columns: 0.5fr;
    border: 1px solid #000;
}
</style>

如果我们继续减少 fr 的值,如 0.01fr ,这是否意味着栅格所占空间也将继续减少到 0.01 呢?

css 复制代码
<style scoped>
#wrapper {
    display: grid;
    grid-template-columns: 0.01fr;
    border: 1px solid #000;
}
</style>

然而并没有。栅格的宽度固定在了最长的单词 excepteaur 的宽度。我们只有使用 min-height/width 才能将这个空间继续缩小。

css 复制代码
<style scoped>

#wrapper #inner {
    min-width: 0;
}
</style>

根据上面的结果,我们有理由进行推理:如果 0.01fr 继续缩小,则栅格所占据的空间会继续缩小,最后 0fr + min-height : 0 将使占据空间缩小为0。

关于 grid, frmin-height/width ,以下是较为直观的解释:

  • 1fr 表示占据主轴中尽量多的空间
  • 0fr 表示占据主轴中尽量少的空间 。这个空间可看作 min-height/widthmin-height/width 的默认值为 auto ,其一般为一个字母/一个单词所占空间。当我们使用 min-height : 0 时,等于覆盖了初始的 auto ,声明主轴所占据的最小空间为0.

因此:

  • grid-template-columns: 1fr; 表示容器内的每个栅格占满宽度 (width : 100%),高度为 100%
  • grid-template-columns: 0fr; 表示容器内的每个栅格占尽量少的宽度 ( min-width ),高度为 100%
  • grid-template-rows: 1fr; 表示容器内的每个栅格占满高度(height : 100%),宽度为 100% .
  • grid-template-rows: 0fr; 表示容器内的每个栅格占尽量少的高度,若 min-height : 0 ,则表示每个栅格所占高度为0,宽度为 100% .

现在让我们回顾最开始的代码:

js 复制代码
<template>
    <button @click="toggle">button</button>
    <div id="expandable" :class="show? 'show': 'hidden'">
      <div>Qui tempor Lorem cillum do ex nisi voluptate consequat cupidatat.</div>
    </div>
    <div>Cillum proident Lorem Lorem nisi aliqua magna Lorem labore laboris mollit.</div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const show = ref(false);

function toggle() {
  show.value = !show.value;
}

</script>

<style scoped>
#expandable {
  display: grid;
  overflow: hidden;
  transition: grid-template-rows 1s ease;
}

#expandable.show {
  grid-template-rows: 1fr;
}

#expandable.hidden {
  grid-template-rows: 0fr;
}

#expandable div {
  min-height: 0;
}

</style>

show = false 时:

  • 容器设置:grid-template-rows: 0fr ,表示其内元素占尽量少的高度。
  • 给折叠面板元素设置 min-height : 0 ,表示其占据的尽量少的高度为0。
  • 容器设置 : overflow:hidden ,表示隐藏折叠面板。

show = true 时: 容器设置:grid-template-rows: 1fr ,表示其内元素占满高度,其效果等同于 height : 100%, 但在栅格布局中其成为了一个确定的值(因为在 grid 布局中,浏览器会解析出每个栅格详细的大小 ,并搭配 min-height/width,max-height/width 计算栅格是否能够放下子元素)。因此能够触发 transition 动画过渡。

总结

transition 失效的原因是 height : 100% 不是一个确定的值,而 grid-template-rows: 1fr; 却是一个确定的值,因此它可以帮我们实现动画过渡。

但要如何使用 grid-template-rows 设置栅格高度为0呢? grid-template-rows: 0fr 搭配 min-height : 0 可以帮上我们的忙。

相关推荐
我的青春不太冷10 分钟前
深入探讨:服务器如何响应前端请求及后端如何查看前端提交的数据
运维·服务器·前端
16年上任的CTO15 分钟前
一文大白话讲清楚webpack进阶——8——Module Federation
前端·webpack·node.js·模块联邦·federation
&白帝&1 小时前
在 Vue 3 中,怎么管理环境变量
前端·javascript·vue.js
橘猫0.o2 小时前
【C语言】static关键字的三种用法
c语言·前端·javascript
我命由我123452 小时前
Tailwind CSS - Tailwind CSS 引入(安装、初始化、配置、引入、构建、使用 Tailwind CSS)
前端·javascript·css·npm·node.js·js
aricvvang3 小时前
web安全 - CSRF
前端·后端·安全
亓才孓4 小时前
[JavaWeb]搜索表单区域
java·前端·css·css3·web
dreadp4 小时前
解锁豆瓣高清海报:深度爬虫与requests进阶之路
前端·爬虫·python·beautifulsoup·github·requests
zm5 小时前
C基础寒假练习(4)
java·前端·数据库