web api ResizeObserver 的使用

作者: 阿巳交不起水电费 | 来源:发表于2024-03-11 14:06 被阅读0次

    window有resize事件,但某些时候我们想监听某个div的resize,这个时候可以使用 ResizeObserver

    以下出自 mdn 文档:

    ResizeObserver 接口监视 Element 内容盒或边框盒或者 SVGElement 边界尺寸的变化。

    备注: 内容盒是盒模型放置内容的部分,这意味着边框盒减去内边距和边框的宽度就是内容盒。边框盒包含内容、内边距和边框。有关进一步阐述,参见盒模型

    ResizeObserver 避免了通过回调函数调整大小时,通常创建的无限回调循环和循环依赖项。它只能通过在后续的帧中处理 DOM 中更深层次的元素来做到这一点。如果它的实现遵循规范,则应在绘制前和布局后调用 resize 事件。

    详细文档请查阅 ResizeObserver

    使用分三步

     // 1.指定resize事件
    let resizeFn = (entries,observer)=>{// 注意这里有参数,包含一些位置,盒子大小信息,对 ResizeObserver 自身的引用
       console.log('div宽高变化将触发此方法')
    }
    let resizeObserver = new ResizeObserver(resizeFn) // 注意初始会调用一次 resizeFn
    
    // 2.指定该resize事件的触发dom
    let resizeBox = document.getElementById('myDom')
    resizeObserver.observe(resizeBox);
    
    // 3.结束对指定dom的监听。
    resizeObserver.unobserve(resizeBox)
    

    ResizeObserver 实例化函数参数文档,也就是上面的 entries,observer - 看这里,我目前感觉用不用都可以,因为也可以用其他方式获取这些信息。

    使用案例

    <template>
        <div ref="resizeDivRefDom" class="resize-div">
            <slot></slot>
        </div>
    </template>
    
    <script>
    export default {
        name: 'resizeDiv',
        props: {
            // 防抖时间
            t: {
                type: Number,
                default: 50
            },
            // 初始第一次是否触发 resize 事件
            firstTrigger: {
                type: Boolean,
                default: true
            }
        },
        data() {
            return {
                resizeObserver: null,
                tickTimer: null,
                firstTime: true, // 标记是否初始渲染
                domWh: { // 当前 dom 信息
                    width: 0,
                    height: 0
                }
            }
        },
        methods: {
            // 根据需求,可以获取任意信息
            getDomInfo(dom) {
                return {
                    width: dom.clientWidth,
                    height: dom.clientHeight
                }
            },
            emitEvent() {
                // console.log('resizeDiv resize...')
                let resizeBox = this.$refs['resizeDivRefDom']
                let oldDomInfo = this.domWh
                let newDomInfo = this.domWh = this.getDomInfo(resizeBox)
                this.$emit('resize', {
                    resizeBox, // 当前 resize 的dom信息
                    oldDomInfo, // resize 前的宽高信息
                    newDomInfo // resize 后的宽高信息
                })
            },
            event_resize() {
                clearTimeout(this.tickTimer)
                this.tickTimer = setTimeout(() => {
                    if (this.firstTime && !this.firstTrigger) { // 初始不触发
                        this.domWh = this.getDomInfo(this.$refs['resizeDivRefDom']) // 初始存储宽高信息
                    } else {
                        this.emitEvent()
                    }
                    this.firstTime = false // 初次渲染成功
                }, this.t)
            },
            addEvents() {
                this.removeEvents()
    
                let resizeBox = this.$refs['resizeDivRefDom']
                // 1.指定resize事件
                this.resizeObserver = new ResizeObserver(this.event_resize) // 会在绘制前和布局后调用 resize 事件,因此不用提前调用 event_resize 方法
                // 2.指定该resize事件的触发dom
                this.resizeObserver.observe(resizeBox);
            },
            removeEvents() {
                let resizeBox = this.$refs['resizeDivRefDom']
                resizeBox && this.resizeObserver?.unobserve(resizeBox)
    
                // 清除定时器
                clearTimeout(this.tickTimer)
            },
            init() {
                this.addEvents()
            },
        },
        mounted() {
            this.init()
        },
        beforeDestroy() {
            this.removeEvents()
        }
    }
    </script>
    
    <style scoped>
    .resize-div {
        width: 100%;
        height: 100%;
    }
    </style>
    

    使用

    <template>
            <div class="mybox" style="height: 20vh;width:100%;">
                <rDiv :firstTrigger="false" @resize="divResizeFn">
                    <div>盒子 resize</div>
                </rDiv>
            </div>
    </template>
    ...
    import resizeDiv from '@/components/resizeDiv/index.vue'
    export default {
        components: {
            rDiv: resizeDiv,
        },
    ...
    

    这里使用vue封装了个 resizeDiv 组件,当外层div盒子宽高发生变化将触发resize事件。

    扩展 - 实现 echarts 的 resize

    vue项目中可以封装为指令,结合 echarts 实现 echarts resize,这样比监听 window 的 resize 精确得多,这里就不贴代码了,代码很简单,参考上面分分钟写出来,大家自己写吧。
    组件使用

    image.png
    使用 v-resize 监听 echart 容器是否宽高发生变化,发生变化后执行该 chart 的resize 事件。【注意:v-resize 是自己写的全局指令,很简单,参考上面代码自行实现。】
    image.png

    若对你有帮助,请点个赞吧,若能打赏不胜感激,谢谢支持!
    本文地址:https://www.jianshu.com/p/53e0fd3bf961,转载请注明出处,谢谢。

    相关文章

      网友评论

        本文标题:web api ResizeObserver 的使用

        本文链接:https://www.haomeiwen.com/subject/hwdmzdtx.html