Vue Affix组件

在vue的项目中经常用到固钉,但是 element-ui 上并没有提供这样的组件可供使用,ant-design-vue 有提供,总不能为了这一个组件再去引入一个组件库吧

下面是一个封装好的 affix 组件,可以放到项目中直接使用

Affix.vue

<template>
    <div class="affix-placeholder" :style="wrapStyle">
        <div :class="{'affix': affixed}" :style="styles">
            <slot></slot>
        </div>
    </div>
</template>
<script>
/**
* @file Affix.vue
* @author v_shenjieping@baidu.com
* @date 2018-12-11 10:09:50
*/
export default {
    props: {
        offset: {
            type: Number,
            default: 0
        },
        onAffix: {
            type: Function,
            default() {}
        },
        boundary: {
            type: String,
            default: ''
        }
    },
    data() {
        return {
            affixed: false,
            styles: {},
            affixedClientHeight: 0,
            wrapStyle: {}
        };
    },
    methods: {
        getScroll(w, top) {
            let ret = w[`page${(top ? 'Y' : 'X')}Offset`];
            const method = `scroll${top ? 'Top' : 'Left'}`;
            if (typeof ret !== 'number') {
                const d = w.document;
                // ie6,7,8 standard mode
                ret = d.documentElement[method];
                if (typeof ret !== 'number') {
                    // quirks mode
                    ret = d.body[method];
                }
            }
            return ret;
        },
        getOffset(element) {
            const rect = element.getBoundingClientRect();
            const body = document.body;
            const clientTop = element.clientTop || body.clientTop || 0;
            const clientLeft = element.clientLeft || body.clientLeft || 0;
            // const clientHeight = element.clientHeight || 0;
            const scrollTop = this.getScroll(window, true);
            const scrollLeft = this.getScroll(window);
            return {
                top: rect.bottom + scrollTop - clientTop - this.affixedClientHeight,
                left: rect.left + scrollLeft - clientLeft
            };
        },
        handleScroll() {
            const scrollTop = this.getScroll(window, true) + this.offsets; // handle setting offset
            const elementOffset = this.getOffset(this.$el);
            if (!this.affixed && scrollTop > elementOffset.top) {
                this.affixed = true;
                this.styles = {
                    top: `${this.offsets}px`,
                    left: `${elementOffset.left}px`,
                    width: `${this.$el.offsetWidth}px`
                };
                this.onAffix(this.affixed);
            }
            // if setting boundary
            if (this.boundary && scrollTop > elementOffset.top) {
                const el = document.getElementById(this.boundary.slice(1));
                if (el) {
                    const boundaryOffset = this.getOffset(el);
                    if ((scrollTop + this.offsets) > boundaryOffset.top) {
                        const top = scrollTop - boundaryOffset.top;
                        this.styles.top = `-${top}px`;
                    }
                }
            }
            if (this.affixed && scrollTop < elementOffset.top) {
                this.affixed = false;
                this.styles = {};
                this.onAffix(this.affixed);
            }
            if (this.affixed && this.boundary) {
                const el = document.getElementById(this.boundary.slice(1));
                if (el) {
                    const boundaryOffset = this.getOffset(el);
                    if ((scrollTop + this.offsets) <= boundaryOffset.top) {
                        this.styles.top = 0;
                    }
                }
            }
        }
    },
    computed: {
        offsets() {
            if (this.boundary) {
                return 0;
            }
            return this.offset;
        }
    },
    mounted() {
        this.affixedClientHeight = this.$el.children[0].clientHeight;
        this.wrapStyle = {height: `${this.affixedClientHeight}px`};
        window.addEventListener('scroll', this.handleScroll);
        window.addEventListener('resize', this.handleScroll);
    },
    beforeDestroy() {
        window.removeEventListener('scroll', this.handleScroll);
        window.removeEventListener('resize', this.handleScroll);
    }
};
</script>
<style lang="sass">
.affix
    position: fixed
</style>

使用方法也是非常简单

test.vue

<template>
    <div class="test">
        <affix>
            <div>这是一个固钉组件</div>
        </affix>
        <affix :offset="40">
            <div>这是一个固钉组件</div>
        </affix>
    </div>
</template>

<script>
import Affix from '@/components/Affix';
export default {
    name: 'test',
    components: {
        Affix
    }
};
</script>

API

参数 说明 类型 默认值
offset 距离窗口顶部达到指定偏移量后触发 Number 0
boundary 设置 Affix 的活动范围,值为affix上级元素的id(可以是父元素,也可以是父元素的父元素...) String(#parent)  
on-affix 固定状态改变时触发的回调函数 Function(affixed)

posted on 2018-12-11 10:36  sjpqy  阅读(9680)  评论(1编辑  收藏  举报

导航