实现高度自动过渡的几种方案

实现流体高度

  • 前言
  • 通用html
  • 一、使用 max-height (简单但是不精准)不推荐
  • 二、 JS动态计算(常规用法)
  • 三、Grid 过渡实现(现代浏览器)推荐
  • 四、使用在项目中(react)
  • 五、使用在项目中(vue3)

前言

有时候在写页面时会遇到这种情况:实现高度不固定的内容,或者是实现高度自动过度的内容。
比如这样的不固定高度的内容
实现高度自动过渡的几种方案_第1张图片
或者这样展示的内容
实现高度自动过渡的几种方案_第2张图片

通用html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>  
        .content-wrapper {
            width: 800px;
            border: 1px solid #ddd;
            border-radius: 8px;
            overflow: hidden;
            margin-bottom: 20px;
        }

        .toggle-btn {
            width: 100%;
            padding: 12px;
            background: #007bff;
            color: white;
            border: none;
            cursor: pointer;
            font-size: 16px;
        }

        .content {
            padding: 1rem;
            background: #f8f9fa;
            border-radius: 4px;
        }

        .toggle-btn:hover {
            background: #0056b3;
        }

        .auto-wrap {
            white-space: normal;
            word-wrap: break-word;
            word-break: break-all;
            max-width: 800px;
            line-height: 1.6;
            text-indent: 2em;
            margin: 0 0 0.2em;
            text-align: left;
        }
    </style>
</head>

<body>
    <div class="content-wrapper">
        <button class="toggle-btn" onclick="changeHeight()">展开/折叠</button>
        <div class="wrapper" id="wrapperId">
            <div class="content">
                <h3>这是标题</h3>
                <p class="auto-wrap">
                    先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。

                <p class="auto-wrap"> 宫中府中,俱为一体;陟罚臧否,不宜异同:若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理;不宜偏私,使内外异法也。
                </p>
                <p class="auto-wrap">侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下:愚以为宫中之事,事无大小,悉以咨之,然后施行,必能裨补阙漏,有所广益。
                </p>
                <p class="auto-wrap"> 将军向宠,性行淑均,晓畅军事,试用于昔日,先帝称之曰“能”,是以众议举宠为督:愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
                </p>
                <p class="auto-wrap">
                    亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之、信之,则汉室之隆,可计日而待也。
                </p>
                <p class="auto-wrap">
                    臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间:尔来二十有一年矣。
                </p>
                <p class="auto-wrap">
                    先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐托付不效,以伤先帝之明;故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
                </p>
                <p class="auto-wrap">
                    愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏。臣不胜受恩感激。
                </p>
                <p class="auto-wrap">今当远离,临表涕零,不知所言。</p>
            </div>
        </div> 
</body>

</html>

一、使用 max-height (简单但是不精准)不推荐

   		 
        

二、 JS动态计算(常规用法)

   		 <style>
	        .wrapper {
	            overflow: hidden;
	            transition: height 0.3s ease;
	            height: 0;
	        }
        </style>
        <script> 
            const jsWrapper = document.getElementById('wrapperId');
            let isJSTransitioning = false;

            function changeHeight () {
                if (isJSTransitioning) return;

                if (jsWrapper.style.height === '0px' || !jsWrapper.style.height) {
                    // 展开
                    jsWrapper.style.height = 'auto';
                    const targetHeight = jsWrapper.scrollHeight + 'px';
                    jsWrapper.style.height = '0px';

                    requestAnimationFrame(() => {
                        isJSTransitioning = true;
                        jsWrapper.style.height = targetHeight;
                    });
                } else {
                    // 折叠
                    isJSTransitioning = true;
                    jsWrapper.style.height = jsWrapper.scrollHeight + 'px';

                    requestAnimationFrame(() => {
                        jsWrapper.style.height = '0px';
                    });
                }
            }

            jsWrapper.addEventListener('transitionend', () => {
                if (jsWrapper.style.height !== '0px') {
                    jsWrapper.style.height = 'auto';
                }
                isJSTransitioning = false;
            });
        </script>

三、Grid 过渡实现(现代浏览器)推荐

这里主要是使用grid-template-rows这个属性来实现的,关于grid用法可以看这里
css grid布局属性详解

   		 <style>
	        .wrapper {
	            display: grid;
	            grid-template-rows: 0fr;
	            transition: grid-template-rows 0.4s ease;
	        }
	
	        .wrapper.expanded {
	            grid-template-rows: 1fr;
	        }
	
	        .content {
	            min-height: 0;
	            /* 关键属性 */
	        }
        </style>
        <script>
            function changeHeight () {
                const wrapper = document.getElementById('wrapperId');
                wrapper.classList.toggle('expanded');
            }
        </script>

四、使用在项目中(react)

这里采用的是第三种方法Grid来实现

css样式文件

	/* CollapseTransition.scss */
	.container {
		width: 800px;
		border: 1px solid #ddd;
		border-radius: 8px;
		overflow: hidden;
		margin-bottom: 20px;
	}
	
	.toggle-btn {
		width: 100%;
		padding: 12px;
		background: #007bff;
		color: white;
		border: none;
		cursor: pointer;
		font-size: 16px;
		outline: 2px solid rgba(0, 123, 255, 0.5);
		outline-offset: 2px;
		transition: background 0.2s;
	}
	
	.toggle-btn:hover {
		background: #0056b3;
	}
	
	.content-wrapper {
		display: grid;
		grid-template-rows: 0fr;
		transition: grid-template-rows 0.3s ease;
	}
	
	.content-wrapper.expanded {
		grid-template-rows: 1fr;
	}
	
	.content {
		min-height: 0;
		padding: 0 16px;
		background: #f8f9fa;
	}
	
	.auto-wrap {
		white-space: normal;
		word-wrap: break-word;
		word-break: break-all;
		max-width: 800px;
		line-height: 1.6;
		text-indent: 2em;
		margin: 0 0 0.2em;
		text-align: left;
		user-select: none;
	}

公共UI组件

// CollapseTransition.jsx
import React, { useState, useRef, useEffect } from 'react';
import '@/assets/common.css';

/**
 * 折叠/展开过渡动画容器组件
 *
 * @param {Object} props - 组件属性
 * @param {string} [props.expandText='点击展开内容'] - 折叠状态时的按钮提示文本
 * @param {string} [props.collapseText='点击折叠内容'] - 展开状态时的按钮提示文本
 * @param {React.ReactNode} props.children - 需要折叠/展开的内容子元素
 *
 * @example
 * 可折叠内容
* */ const CollapseTransition = ({ expandText = '点击展开内容', collapseText = '点击折叠内容', children }) => { // 控制展开/折叠状态 const [isExpanded, setIsExpanded] = useState(false); const contentWrapperRef = useRef(null); // 处理过渡动画结束事件 useEffect(() => { const wrapper = contentWrapperRef.current; if (!wrapper) return; const handleTransitionEnd = () => { wrapper.style.overflow = isExpanded ? 'visible' : 'hidden'; }; wrapper.addEventListener('transitionend', handleTransitionEnd); return () => { wrapper.removeEventListener('transitionend', handleTransitionEnd); }; }, [isExpanded]); // 切换展开/折叠状态 const toggleExpand = () => { setIsExpanded(!isExpanded); }; return ( <div className="container"> <button className="toggle-btn" onClick={toggleExpand} > {isExpanded ? collapseText : expandText} </button> <div ref={contentWrapperRef} className={`content-wrapper ${isExpanded ? 'expanded' : ''}`} style={{ overflow: 'hidden' }} // 初始隐藏溢出内容 > <div className="content">{children}</div> </div> </div> ); }; export default CollapseTransition;

在项目中使用

// demo.jsx
import CollapseTransition from '@/components/CollapseTransition';

const demo = () => {
	return (
		<div>
			<CollapseTransition
				expandText="展开《出师表》"
				collapseText="折叠《出师表》"
				containerStyle={{ marginBottom: 20 }}
			>
				<p className="auto-wrap">
					先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
				</p>
				<p className="auto-wrap">
					宫中府中,俱为一体;陟罚臧否,不宜异同:若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理;不宜偏私,使内外异法也。
				</p>
				<p className="auto-wrap">
					侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下:愚以为宫中之事,事无大小,悉以咨之,然后施行,必能裨补阙漏,有所广益。
				</p>
				<p className="auto-wrap">
					将军向宠,性行淑均,晓畅军事,试用于昔日,先帝称之曰“能”,是以众议举宠为督:愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
				</p>
				<p className="auto-wrap">
					亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之、信之,则汉室之隆,可计日而待也。
				</p>
				<p className="auto-wrap">
					臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间:尔来二十有一年矣。
				</p>
				<p className="auto-wrap">
					先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐托付不效,以伤先帝之明;故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
				</p>
				<p className="auto-wrap">
					愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏。臣不胜受恩感激。
				</p>
				<p className="auto-wrap">今当远离,临表涕零,不知所言。</p>
			</CollapseTransition>
		</div>
	);
};

export default demo;

五、使用在项目中(vue3)

css样式文件

	.container {
		width: 800px;
		border: 1px solid #ddd;
		border-radius: 8px;
		overflow: hidden;
	}
	
	.toggle-btn {
		width: 100%;
		padding: 12px;
		background: #007bff;
		color: white;
		border: none;
		cursor: pointer;
		font-size: 16px;
		outline: 2px solid rgba(0, 123, 255, 0.5);
		outline-offset: 2px;
		transition: background 0.2s;
	}
	
	.toggle-btn:hover {
		background: #0056b3;
	}
	
	.content-wrapper {
		display: grid;
		grid-template-rows: 0fr;
		transition: grid-template-rows 0.3s ease;
	}
	
	.content-wrapper.expanded {
		grid-template-rows: 1fr;
	}
	
	.content {
		min-height: 0;
		padding: 0 16px;
		background: #f8f9fa;
	}
	
	.auto-wrap {
		white-space: normal;
		word-wrap: break-word;
		word-break: break-all;
		max-width: 800px;
		line-height: 1.6;
		text-indent: 2em;
		margin: 0 0 0.2em;
		text-align: left;
		user-select: none;
	}

公共组件

<!-- CollapseTransition.vue -->
<template>
    <div class="container">
        <button class="toggle-btn" @click="toggleExpand">
            {{ isExpanded ? collapseText : expandText }}
        </button>
        <div ref="contentWrapper" class="content-wrapper" :class="{ expanded: isExpanded }" @transitionend="handleTransitionEnd">
            <div class="content">
                <slot />
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref, watch } from 'vue'

const props = defineProps({
    expandText: {
        type: String,
        default: '点击展开内容'
    },
    collapseText: {
        type: String,
        default: '点击折叠内容'
    },
})

const isExpanded = ref(false)
const contentWrapper = ref(null)

// 处理过渡动画结束事件
const handleTransitionEnd = () => {
    if (!contentWrapper.value) return
    contentWrapper.value.style.overflow = isExpanded.value ? 'visible' : 'hidden'
}

// 切换展开状态
const toggleExpand = () => {
    isExpanded.value = !isExpanded.value
}

// 初始化时设置 overflow
watch(() => contentWrapper.value,
    (newVal) => {
        if (newVal) {
            newVal.style.overflow = 'hidden'
        }
    },
    { immediate: true }
)
</script>

<style >
@import "../assets/common.css";
</style>

使用

<template>
    <div class="demo-container">
        <CollapseTransition expand-text="展开《出师表》" collapse-text="折叠《出师表》" >
            <p className="auto-wrap">
                先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
            </p>
            <p className="auto-wrap">
                宫中府中,俱为一体;陟罚臧否,不宜异同:若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理;不宜偏私,使内外异法也。
            </p>
            <p className="auto-wrap">
                侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下:愚以为宫中之事,事无大小,悉以咨之,然后施行,必能裨补阙漏,有所广益。
            </p>
            <p className="auto-wrap">
                将军向宠,性行淑均,晓畅军事,试用于昔日,先帝称之曰“能”,是以众议举宠为督:愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
            </p>
            <p className="auto-wrap">
                亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之、信之,则汉室之隆,可计日而待也。
            </p>
            <p className="auto-wrap">
                臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间:尔来二十有一年矣。
            </p>
            <p className="auto-wrap">
                先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐托付不效,以伤先帝之明;故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
            </p>
            <p className="auto-wrap">
                愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏。臣不胜受恩感激。
            </p>
            <p className="auto-wrap">今当远离,临表涕零,不知所言。</p>
        </CollapseTransition>
    </div>
</template>

<script setup>
import CollapseTransition from '@/components/CollapseTransition.vue'
</script>

<style>

</style>

你可能感兴趣的:(html5,javascript,css3,vue.js,scss,react.js)