Skip to content

Instantly share code, notes, and snippets.

@b4n92uid
Created November 10, 2020 15:45
Show Gist options
  • Save b4n92uid/5d18c6ca9d60abd84482d2f1a5d42745 to your computer and use it in GitHub Desktop.
Save b4n92uid/5d18c6ca9d60abd84482d2f1a5d42745 to your computer and use it in GitHub Desktop.
[Vue] StickyArea
<template>
<div class="sticky-area" :style="style" ref="wrap">
<div ref="top"></div>
<div ref="body">
<slot></slot>
</div>
<div ref="bottom"></div>
</div>
</template>
<script>
import { debounce } from "lodash";
function getElementOffset(el) {
var rect = el.getBoundingClientRect();
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
return {
top: rect.top + scrollTop,
left: rect.left + scrollLeft
};
}
export default {
props: {
offsetTop: {
type: Number,
default: 16
},
offsetBottom: {
type: Number,
default: 16
},
offsetAppBar: {
type: Number,
default: 56
},
disabled: {
type: Boolean,
default: false
}
},
data() {
return {
lastPageOffset: 0,
initialPos: {
top: 0,
bottom: 0
},
positionY: 0
};
},
mounted() {
const vm = this;
this.initialPos = getElementOffset(vm.$refs.wrap);
this.scrollHandler = debounce(function() {
if (vm.disabled) return;
if (!vm.$refs.bottom) return;
const isScrollDown = vm.lastPageOffset - window.pageYOffset < 0;
const heightOverflow = window.innerHeight - vm.$refs.body.clientHeight;
vm.lastPageOffset = window.pageYOffset;
if (heightOverflow > 0) {
vm.positionY = window.pageYOffset - vm.initialPos.top;
vm.positionY += vm.offsetTop;
if (!isScrollDown) vm.positionY += vm.offsetAppBar;
} else if (isScrollDown) {
const offset = getElementOffset(vm.$refs.bottom);
if (window.pageYOffset + window.innerHeight > offset.top) {
vm.positionY =
window.pageYOffset - vm.initialPos.top + heightOverflow;
vm.positionY -= vm.offsetBottom;
}
} else {
const offset = getElementOffset(vm.$refs.top);
if (window.pageYOffset < offset.top) {
vm.positionY = window.pageYOffset - vm.initialPos.top;
vm.positionY += vm.offsetTop;
vm.positionY += vm.offsetAppBar;
}
}
vm.positionY = Math.max(vm.positionY, 0);
vm.positionY = Math.min(
vm.positionY,
vm.parentInnerHeight() - vm.$refs.wrap.clientHeight
);
}, 50);
this.scrollHandler();
window.addEventListener("scroll", this.scrollHandler, { passive: true });
},
destroyed() {
window.removeEventListener("scroll", this.scrollHandler);
},
computed: {
style() {
return {
transform: `translateY(${this.positionY}px)`
};
}
},
methods: {
parentInnerHeight() {
const parent = this.$refs.wrap.parentElement;
const style = getComputedStyle(parent);
return (
parent.clientHeight -
parseInt(style.paddingTop) -
parseInt(style.paddingBottom)
);
}
},
watch: {
disabled() {
if (this.disabled) this.positionY = 0;
else this.initialPos = getElementOffset(this.$refs.wrap);
}
}
};
</script>
<style lang="scss">
.sticky-area {
transition: transform 200ms ease-in-out;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment