CSS 网格布局(grid)-展开收起动画(十一)

845 阅读5分钟
  • 在实现基本交互功能时适当的使用动画, 往往会达到锦上添花的效果
  • 在 css 中如果要实现从一个样式过渡到另一个样式, 一般需要提供给 css 解析器两个值, 解析器可以根据具体情况在一段时间内完成由一个值变到另一个值的动画效果
  • 如: 20% => 100%0 => 100px 或者 0deg => 90deg, 需要注意的是如果值是自动计算的将无法实现过渡效果
  • 今天我们来看一下怎样实现展开收起的动画实现, 实际上本文以展开/收起的效果为例子, 说一下怎样从 height: auto 过渡到 height: 0;
  • 我们主要说 4 种实现方式, 都有各自的优缺点, 选择适合的方式使用就可以了
    • 基础实现, 通过设置 height: 0, 实现展开收起但没有动画效果
    • JS计算高度, 通过计算高度实现真实高度和0之间的过渡
    • max-height, 通过使用 max-height, 模拟实现高度的变化
    • 网格布局(grid), 使用网格中的 fr 单位实现
  • 下面我们就一步一步的实现, 如果想直接看示例效果, 可以直接看后面的内容

实现一个基本结构

image.png

<div class="app">
    <input type="checkbox" id="checkbox">
    <label for="checkbox"></label>
    <div class="content">
        <ul>
            <li>item1</li>
            <li>item2</li>
            <li>item3</li>
            <li>item4</li>
            <li>item5</li>
            <li>item6</li>
            <li>item7</li>
            <li>item8</li>
        </ul>
    </div>
</div>
.app {
    margin: 20px;
    border: 1px solid #ccc;
    line-height: 38px;

    // 隐藏input, 只是使用它的状态不需要显示出来
    input {
        position: absolute;
        top: -1000px;
    }

    label {
        cursor: pointer;
        margin-left: 15px;

        // 增加一个提示文字, 标识现在是展开还是收起
        &::before {
            content: '收起';
        }

        // 实现一个小剪头, 同样标识展开/收起状态
        &::after {
            $size: 10px;
            content: '';
            display: inline-block;
            border: $size solid transparent;
            border-top-color: #4C84FF;
            transform: rotate(180deg);
            position: relative;
            top: $size;
            transform-origin: 50% 25%;
            transition: all 0.3s;
            margin-left: $size;
        }
    }

    .content {
        overflow: hidden; // content 不占位时, 子元素也不显示
        transition: all 1s; // 指定怎样过渡, 在后边才会用到

        // 一些显示效果, 可不用太关心
        li {
            border: 1px solid #ccc;
            margin: -1px;
            margin-top: 0;
            padding: 0 30px;
        }
    }

    // 使用 checked 伪类, 在 checkbox 选中时, 应用下面的样式
    input:checked {
        &~label {
            &::before {
                content: '展开';
            }

            &::after {
                transform: rotate(0deg);
            }
        }
    }
}
  • 我在代码中写了一些注释, 比较重要的地方都简单的说明了一下, 可对照着效果看一下
  • 其中有一些知识点挺不错的, 可以了解一下, 可能在工作中也会用到
    • 在代码中通过 checkbox 来设置一个状态(可以理解为开/关), 并通过伪类去使用, 是 html 和 css 结合使用的一种方式, 挺巧妙的
    • 代码中还通过 border 实现了一个小三角形(22行), 并且实现了旋转功能(28和58行), 需要注意的点: 1. 三角形在上部, 怎办移动到中心, 2. 三角形不是在中心, 怎样设置旋转中心
  • 还有一些是处理样式的, 我们不需要太关心, 关注核心的处理逻辑就可以了

基础实现(一)

.base {
    input:checked~.content {
        height: 0;
    }
}
  • 当 checkbox 处于 checked 时, 把宽度设置为 0, 就可以实现收起的效果, 但没有动画效果
  • 在基础结构中还设置了 overflow: hidden; 其实在这里才有用, 需要注意一下

JS计算高度(二)

// const jsDom = document.querySelector('.js');
const jsDom = document.querySelector('.app');
const checkboxDom = jsDom.querySelector('input');
const contentDom = jsDom.querySelector('.content');
const ulDom = jsDom.querySelector('.content ul');

const handleChange = () => {
    if (checkboxDom.checked) {
        contentDom.style.height = 0;
        return;
    }

    contentDom.style.height = `${ulDom.clientHeight}px`;
}

checkboxDom.addEventListener('change', handleChange);
handleChange();
  • 在基础结构中还设置了 overflow: hidden;transition: all 1s; 在这里和之后都会用到, 它们的作用就不解释了, 相信你是知道的
  • 通过 js 监听 checkbox 的 change 事件, 当状态为 checked 时设置高度为 0, 否则重新获取高度并设置
  • 这样就可以从高度 0 过渡到一个固定的高度了, 这样动画效果就出来了
  • 优点: 基本上可以处理所有的场景, 效果也相对好
  • 缺点: 需要写 js 代码, 需要获取元素高度

max-height(三)

.max-height {
    .content {
        max-height: 500px;
    }

    input:checked~.content {
        max-height: 0;
    }
}
  • 通过 max-height 过渡效果来实现动画, 因为 max-height 不是实际的高度, 效果无法保证
  • 当然设置使用 height 也是可以的, max-height 的优点是可以处理高度变化的场景(但也不能过大)
  • 优点: 代码相对简单, 并能实现动画效果
  • 缺点: 因为不是设置真实高度, max-height 要求和真实高度不能差太多, 要不然效果会很差(即: 高度固定或者高度变化不会太大)

网格布局(四)

.grid {
    .content {
       display: grid;
       grid-template-rows: 1fr;
    }

    ul {
        min-height: 0;
    }

    input:checked~.content {
        grid-template-rows: 0fr;
    }
}
  • fr 是网格布局中的一个新单位, 需要注意的是别忘了设置 min-height: 0; 即: 子元素增加 min-height 属性
  • 这个方法是纯css实现的, 并且支持的动画效果也很好
  • 优点: 代码相对简单, 能完好的实现动画效果
  • 缺点: 在 safari 浏览器中存在兼容性, 动画会失效, 但也不影响用户使用

代码示例

小结

  • 实现高度变化的动画效果, 实际高度可以根据内容变化(不要求固定高度)
  • JS计算高度, 通过计算高度实现真实高度和 0 之间的过渡
  • max-height, 通过使用 max-height, 模拟实现高度的变化
  • 网格布局(grid), 使用网格中的 fr 单位实现

相关文档