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
| <template> <div class="affix-placeholder" :style="wrapStyle"> <div :class="{ affix: affixed }" :style="styles"> <slot></slot> </div> </div> </template> <script> 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; ret = d.documentElement[method]; if (typeof ret !== "number") { 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 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; 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 (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 > .affix { position: fixed; } </style>
|