index.js 4.33 KB
Newer Older
李嘉林 committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
import { VantComponent } from '../common/component';
const ROOT_ELEMENT = '.van-sticky';
VantComponent({
    props: {
        zIndex: {
            type: Number,
            value: 99
        },
        offsetTop: {
            type: Number,
            value: 0,
            observer: 'observeContent'
        },
        disabled: {
            type: Boolean,
            observer(value) {
                if (!this.mounted) {
                    return;
                }
                value ? this.disconnectObserver() : this.initObserver();
            }
        },
        container: {
            type: null,
            observer(target) {
                if (typeof target !== 'function' || !this.data.height) {
                    return;
                }
                this.observeContainer();
                this.updateFixed();
            }
        }
    },
    data: {
        height: 0,
        fixed: false
    },
    methods: {
        getContainerRect() {
            const nodesRef = this.data.container();
            return new Promise(resolve => nodesRef.boundingClientRect(resolve).exec());
        },
        initObserver() {
            this.disconnectObserver();
            this.getRect(ROOT_ELEMENT).then((rect) => {
                this.setData({ height: rect.height });
                wx.nextTick(() => {
                    this.observeContent();
                    this.observeContainer();
                });
            });
        },
        updateFixed() {
            Promise.all([this.getRect(ROOT_ELEMENT), this.getContainerRect()]).then(([content, container]) => {
                this.setData({ height: content.height });
                this.containerHeight = container.height;
                wx.nextTick(() => {
                    this.setFixed(content.top);
                });
            });
        },
        disconnectObserver(observerName) {
            if (observerName) {
                const observer = this[observerName];
                observer && observer.disconnect();
            }
            else {
                this.contentObserver && this.contentObserver.disconnect();
                this.containerObserver && this.containerObserver.disconnect();
            }
        },
        observeContent() {
            const { offsetTop } = this.data;
            this.disconnectObserver('contentObserver');
            const contentObserver = this.createIntersectionObserver({
                thresholds: [0.9, 1]
            });
            contentObserver.relativeToViewport({ top: -offsetTop });
            contentObserver.observe(ROOT_ELEMENT, res => {
                if (this.data.disabled) {
                    return;
                }
                this.setFixed(res.boundingClientRect.top);
            });
            this.contentObserver = contentObserver;
        },
        observeContainer() {
            if (typeof this.data.container !== 'function') {
                return;
            }
            const { height } = this.data;
            this.getContainerRect().then((rect) => {
                this.containerHeight = rect.height;
                this.disconnectObserver('containerObserver');
                const containerObserver = this.createIntersectionObserver({
                    thresholds: [0.9, 1]
                });
                this.containerObserver = containerObserver;
                containerObserver.relativeToViewport({
                    top: this.containerHeight - height
                });
                containerObserver.observe(ROOT_ELEMENT, res => {
                    if (this.data.disabled) {
                        return;
                    }
                    this.setFixed(res.boundingClientRect.top);
                });
            });
        },
        setFixed(top) {
            const { offsetTop, height } = this.data;
            const { containerHeight } = this;
            const fixed = containerHeight && height
                ? top >= height - containerHeight && top < offsetTop
                : top < offsetTop;
            this.$emit('scroll', {
                scrollTop: top,
                isFixed: fixed
            });
            this.setData({ fixed });
        }
    },
    mounted() {
        this.mounted = true;
        if (!this.data.disabled) {
            this.initObserver();
        }
    },
    destroyed() {
        this.disconnectObserver();
    }
});