用vue写轮子的一些心得(十一)——sticky粘滞组件

需求分析

  • 支持页面向下滚动时组件会自动粘滞在窗口顶部;
  • 可配置粘滞在窗口顶部的间距;

 

方法实现

一、思路:

1、通过窗口顶部到文档顶部的距离(window.scrollY)和sticky组件到窗口顶部的距离(top + window.scrollY)加上窗口顶部到文档顶部的距离(window.scrollY)来比较,前面大于后面则吸顶,后面大于前面则不吸顶。

window.scrollY > top + window.scrollY

 

二、方法实现

1、sticky外部组件传参定义:

<template>
    <div class="box">
        <div>内容一</div>
        <div>内容一</div>
        <div>内容一</div>
        <div>内容一</div>
        <div>内容一</div>
        <div>内容一</div>
        <t-sticky :distance="30"> //sticky距离顶部可配置的值
            <div class="sticky-box">这是一段很长的代码这是一段很长的代码这是一段很长的代码</div>
        </t-sticky>
        <div>内容一</div>
        <div>内容一</div>
        <div>内容一</div>
        <div>内容一</div>
        <div>内容一</div>
        <div>内容一</div>
        <div>内容一</div>
        <div>内容一</div>
        <div>内容一</div>
    </div>
</template>

2、sticky内部组件实现

1、主要通过 _windowScrollHandler() 函数来判断是否粘滞顶部,如果粘滞则改变css的类(fixed),不粘滞则取消这个类。

2、可配置的粘滞在窗口顶部的间距通过组件外部传参(distance) 在 _windowScrollHandler() 函数中的 if 判断那里加上distance参数进行动态计算。

if (window.scrollY > top - this.distance)

<template>
    <div class="t-sticky-content" ref="wrapper" :style="{height}">
        <div class="t-sticky-content-box" :class="classes" :style="{width, left, top}">
            <slot></slot>
        </div>
    </div>
</template>
export default {
        name: 't-sticky',
        props: {
            distance: {
                type: Number,
                default: 0
            }
        },
        data() {
            return {
                sticky: false,
                left: undefined,
                width: undefined,
                height: undefined,
                top: undefined,
            }
        },

        computed: {
            classes() {
                return {
                    sticky: this.sticky
                }
            }
        },

        mounted() {
            this.windowScrollHandler = this._windowScrollHandler.bind(this)
            window.addEventListener('scroll', this.windowScrollHandler)
        },
        beforeDestroy() {
            window.removeEventListener('scroll', this.windowScrollHandler)
        },

        methods: {
            offsetTop() {
                let {top} = this.$refs.wrapper.getBoundingClientRect()
                return {top: top + window.scrollY}
            },
            _windowScrollHandler() {
                let {top} = this.offsetTop()
                if (window.scrollY > top - this.distance) {
                    let {height, left, width} = this.$refs.wrapper.getBoundingClientRect()
                    this.height = height + 'px'
                    this.width = width + 'px'
                    this.top = this.distance + 'px'
                    this.left = left + 'px'
                    this.sticky = true
                } else {
                    this.height = undefined
                    this.width = undefined
                    this.top = undefined
                    this.left = undefined
                    this.sticky = false
                }
            },
        }
    }

3、一些需要注意的坑 

1、在组件fixed定位时,组件会脱离文档流,需要在组件原来的位置留出高度,不然在滚动的时候会出现BUG。

2、在组件fiexd定位时,需要动态计算组件未粘滞时距离左右视口的距离(left,right)及组件宽度(width)。

3、在页面销毁的时候要关闭scroll监听,在beforeDestroy钩子里关闭,否则会影响性能以及会有BUG。

 

完整代码可参考我的GitHub:https://github.com/A-Tione/tione/blob/master/src/sticky/sticky.vue

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值