简单且实用的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 可以帮上我们的忙。

相关推荐
y先森8 分钟前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy8 分钟前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu108301891111 分钟前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿1 小时前
CSS查缺补漏(补充上一条)
前端·css
吃杠碰小鸡2 小时前
commitlint校验git提交信息
前端
虾球xz3 小时前
游戏引擎学习第20天
前端·学习·游戏引擎
我爱李星璇3 小时前
HTML常用表格与标签
前端·html
疯狂的沙粒3 小时前
如何在Vue项目中应用TypeScript?应该注意那些点?
前端·vue.js·typescript
小镇程序员3 小时前
vue2 src_Todolist全局总线事件版本
前端·javascript·vue.js
野槐3 小时前
前端图像处理(一)
前端