前端如何优雅实现0到auto的高度过渡(隐藏款-程序员BUFF派送福利)

举报
yd_244540595 发表于 2024/10/31 11:25:42 2024/10/31
【摘要】 作者:Linsk各位好,相信大家都知道,Chrome 已经支持了 0 到 auto 的高度过渡。但是很多人反映这种特性太新了,出于兼容考虑用不了的。而实际上 calc-size 是可以渐进增强的。今天我就给大家表演一下,0 到 auto 的渐进增强兼容所有浏览器。我们先搭建一个空白工程化项目这个示例工程。是一个计数器。import * as React from "react";import...

作者:Linsk

各位好,相信大家都知道,Chrome 已经支持了 0 到 auto 的高度过渡。但是很多人反映这种特性太新了,出于兼容考虑用不了的。而实际上 calc-size 是可以渐进增强的。今天我就给大家表演一下,0 到 auto 的渐进增强兼容所有浏览器。

我们先搭建一个空白工程化项目

这个示例工程。是一个计数器。

import * as React from "react";
import { Component } from "react";

interface AppProps { }
interface AppState {
    count: number;
}

export default class App extends Component<AppProps, AppState> {
    constructor(props: AppProps) {
        super(props);
        this.increase = this.increase.bind(this);
        this.decrease = this.decrease.bind(this);
        this.state = {
            count: 0
        };
    }

    private decrease() {
        this.setState({
            count: this.state.count - 1
        });
    }

    private increase() {
        this.setState({
            count: this.state.count + 1
        });
    }

    render() {
        var { count } = this.state;
        return <>
            <h1>Hello World!</h1>
            <div>
                <button type="button" onClick={this.decrease}>-</button>
                {count}
                <button type="button" onClick={this.increase}>+</button>
            </div>
        </>;
    }
}

接下来我们创建一个组件

这个组件有展开和收起 2 种状态。修改传入的属性,可以切换 className

import * as React from "react";
import { Component } from "react";
import "./CollapseBody.scss";

export interface CollapseBodyProps {
    open?: boolean;
}

export class CollapseBody extends Component<CollapseBodyProps> {
    render() {
        let { open, children } = this.props;
        let classList = ["collapse-body"];
        if(open) {
            classList.push('collapse-body_open');
        } else {
            classList.push('collapse-body_close');
        }
        return <div className={classList.join(" ")}>
            {children}
        </div>;
    }
}

接下来我们引入组件

 直接在刚刚的计数器上改一下,这样在计数的时候就会切换展开收起状态。

import * as React from "react";
import { Component } from "react";
import { CollapseBody } from "./Components/Collapse/CollapseBody";

interface AppProps { }
interface AppState {
    count: number;
}

export default class App extends Component<AppProps, AppState> {
    constructor(props: AppProps) {
        super(props);
        this.increase = this.increase.bind(this);
        this.decrease = this.decrease.bind(this);
        this.state = {
            count: 0
        };
    }

    private decrease() {
        this.setState({
            count: this.state.count - 1
        });
    }

    private increase() {
        this.setState({
            count: this.state.count + 1
        });
    }

    render() {
        var { count } = this.state;
        return <>
            <h1>Hello World!</h1>
            <div>
                <button type="button" onClick={this.decrease}>-</button>
                {count}
                <button type="button" onClick={this.increase}>+</button>
            </div>
            <CollapseBody open={count % 2 === 0}>
                asdasdas
            </CollapseBody>
        </>;
    }
}

接下来我们看看效果

我们可以看到按钮时 class 在切换

接下来编写样式

展开时高度是 calc-size (auto , size);收起时,高度是 0;在加以 transition 和 overflow 就完成了。

我们试一下,在高版本正常过渡,低版本由于不支持 calc-size (auto, size),于是高度为默认值 auto,没有过渡动画,但是不影响使用。

.collapse-body {
    transition: height .3s;
    overflow-y: hidden;
    height: calc-size(auto, size);
}

.collapse-body_close {
    height: 0;
}

overflow 和 display 优化

上面代码虽然能用但是还不够完美,有 2 个问题。

  • 首先容器含有 overflow:hidden,会导致内部内容被截断,如果内部内容还有 box-shadow,很容易被截掉。最好是只在过渡时含有 overflow:hidden。
  • 还有收起后应当含有 disaplay:none,否则收起后仍然占据体积,在一些情况下会出现不必要的滚动条。

我们可以使用 animation 进行改造。

.collapse-body_close {
    display: none;
}

@supports(height: calc-size(auto, size)) {
    .collapse-body_open {
        animation: collapseBodyIn .3s;
    }

    .collapse-body_close {
        animation: collapseBodyOut .3s;
    }
}

@keyframes collapseBodyIn {
    from {
        overflow-y: hidden;
        height: 0;
    }

    to {
        overflow-y: hidden;
        height: calc-size(auto, size);
    }
}

@keyframes collapseBodyOut {
    from {
        overflow-y: hidden;
        display: block;
        height: calc-size(auto, size);
    }

    to {
        overflow-y: hidden;
        display: block;
        height: 0;
    }
}

出现动画

上面示例首次出现时也有动画,假如不想要首次出现的动画,只有切换时才有动画,可以在组件上控制。

import * as React from "react";
import { Component } from "react";
import "./CollapseBody.scss";

export interface CollapseBodyProps {
    open?: boolean;
}

export class CollapseBody extends Component<CollapseBodyProps> {
    private first = true;
    componentDidMount(): void {
        this.first = false;
    }
    render() {
        let { open, children } = this.props;
        let classList = ["collapse-body"];
        if(!open) {
            classList.push('collapse-body_close');
        } else if(!this.first) {
            classList.push('collapse-body_open');
        }
        return <div className={classList.join(" ")}>
            {children}
        </div>;
    }
}

最后看看效果

非常的完美

最后我们再看看低版本浏览器的效果

先点构建,我这里配置的构建脚本已经,包含了语法转义和 polyfill,也用了 anujs,因此 react 的兼容性可以不用操心,我们只看 CSS 效果。

我们直接上难度挑战 IE6,我们发现在在 IE6 中没有过渡效果,而是直接隐藏显示,并不影响实际使用,非常地优雅

视频版:【前端如何优雅实现 0到auto 的高度过渡-哔哩哔哩】 b23.tv/KBx7UoQ

…………………………………………………………

“爱码士”集结号,唤醒“猿”气超能力 ——福利暖冬免费派送活动火热进行中

代码工具VIP会员、文创“爱码士”礼包、开心麻花门票等➡派送通道~

😋程序员狂欢继续~~~

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。