Bootstrap

「技术方案」监听字体大小实现页面高度自适应

一、监听DOM元素的API

要监听字体大小并根据其变化调整高度,可以使用以下方法:

1. 使用 ResizeObserver

ResizeObserver 是一个现代浏览器支持的 API,用于监听 DOM 元素的尺寸变化。通过监听字体大小变化(会影响元素高度),可以动态调整高度。

实现代码
attached() {
    const paragraphInstance = this.ref('paragraph');
    const paragraphElement = paragraphInstance.el; // 获取 DOM 元素

    // 创建 ResizeObserver 实例
    const resizeObserver = new ResizeObserver(() => {
        const computedStyle = window.getComputedStyle(paragraphElement);
        const fontSize = parseFloat(computedStyle.fontSize); // 获取字体大小
        const newHeight = fontSize * 1.5; // 假设高度为字体大小的1.5倍

        paragraphElement.style.height = `${newHeight}px`; // 动态设置高度
        console.log('字体大小:', fontSize, '调整后高度:', newHeight);
    });

    // 开始观察
    resizeObserver.observe(paragraphElement);

    // 在组件销毁时断开观察器(可选)
    this.resizeObserver = resizeObserver; // 保存以便销毁时使用
},
detached() {
    // 断开 ResizeObserver
    this.resizeObserver?.disconnect();
}

2. 通过 MutationObserver 检测样式变化

如果 ResizeObserver 不满足需求,可以使用 MutationObserver,监测 style 属性变化,从而捕获字体大小的调整。

实现代码
attached() {
    const paragraphInstance = this.ref('paragraph');
    const paragraphElement = paragraphInstance.el;

    const observer = new MutationObserver(() => {
        const computedStyle = window.getComputedStyle(paragraphElement);
        const fontSize = parseFloat(computedStyle.fontSize);
        const newHeight = fontSize * 1.5;

        paragraphElement.style.height = `${newHeight}px`;
        console.log('字体大小:', fontSize, '调整后高度:', newHeight);
    });

    observer.observe(paragraphElement, {
        attributes: true, // 监听属性变化
        attributeFilter: ['style'], // 限定监听的属性
    });

    this.mutationObserver = observer;
},
detached() {
    // 停止监听
    this.mutationObserver?.disconnect();
}

3. 基于 window.getComputedStyle 的定时轮询(备选方案)

如果对性能要求不高,或者需要兼容不支持上述方法的浏览器,可以定期轮询检查字体大小是否变化。

实现代码
attached() {
    const paragraphInstance = this.ref('paragraph');
    const paragraphElement = paragraphInstance.el;

    this.checkFontSize = setInterval(() => {
        const computedStyle = window.getComputedStyle(paragraphElement);
        const fontSize = parseFloat(computedStyle.fontSize);
        const newHeight = fontSize * 1.5;

        paragraphElement.style.height = `${newHeight}px`;
        console.log('字体大小:', fontSize, '调整后高度:', newHeight);
    }, 500); // 每500ms检查一次
},
detached() {
    // 清除定时器
    clearInterval(this.checkFontSize);
}

选择方案

  • 优先使用 ResizeObserver:性能好,专注于尺寸变化。
  • MutationObserver:适合监测样式属性变化,但要限制监听范围,避免性能问题。
  • 轮询:简单易实现,但性能可能较低,仅作为备选方案。

二、实现页面高度随字体变化自适应

以上方法无法实现随着字体变化高度的完美变化,总会存在点问题
所以我们在 this.nextTick() 调用更新高度的方法 updateSwiperHeight

// 父组件
updateHeight() {
    this.nextTick(() => {
        this.updateSwiperHeight();
    });
}

// 更新高度的方法
updateSwiperHeight(data = {}) {
    const isLilac = this.data.get('styleVarient') === 'lilac';
    // 取row-content的高度给cos-swiper-content
    const rowContentEl = this.ref(`swiperItem_${data.index || 0}`)?.el.querySelector('.cos-row');
    const swiperEl = this.ref('swiper')?.el.querySelector('.cos-swiper-content');
    if (!rowContentEl || !swiperEl) {
        return;
    }
    // lilac样式下顶部有15px外间距
    swiperEl.style.height = `${rowContentEl.clientHeight + (isLilac ? 15 : 0)}px`;
}

三、整合代码

1、在字体所在组件(子组件)监听字体变化

attached() {
    const paragraphInstance = this.ref('paragraph');
    const paragraphElement = paragraphInstance.el;
    // ResizeObserver 监听字体变化后,更新高度
    const resizeObserver = new ResizeObserver(() => {
        this.fire('update-height'); // 触发父组件更新
    });
    resizeObserver.observe(paragraphElement);
}

detached() {
    this.resizeObserver?.disconnect();
}

2、在swiper高度所在组件(父组件)绑定方法

// 模版部分
<template>
    <div>
        <p>父组件的 swiperHeight:{{ swiperHeight }}</p>
        <child-component swiper-height="{{ swiperHeight }}" on-update-height="updateHeight" />
    </div>
</template>
// 逻辑部分
updateHeight() {
    this.nextTick(() => {
        this.updateSwiperHeight();
    });
}

// 更新高度的方法
updateSwiperHeight(data = {}) {
    const isLilac = this.data.get('styleVarient') === 'lilac';
    // 取row-content的高度给cos-swiper-content
    const rowContentEl = this.ref(`swiperItem_${data.index || 0}`)?.el.querySelector('.cos-row');
    const swiperEl = this.ref('swiper')?.el.querySelector('.cos-swiper-content');
    if (!rowContentEl || !swiperEl) {
        return;
    }
    // lilac样式下顶部有15px外间距
    swiperEl.style.height = `${rowContentEl.clientHeight + (isLilac ? 15 : 0)}px`;
}
;