affix.vue 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. <template>
  2. <div ref="placeholderNode" :style="{ ...placeholderNodeStyle }">
  3. <div ref="fixedNode" :style="{ ...fixedNodeStyle }">
  4. <slot />
  5. </div>
  6. </div>
  7. </template>
  8. <script>
  9. import { TRIGGER_EVENTS, getDefaultTarget } from './utils'
  10. import addDOMEventListener from 'add-dom-event-listener'
  11. import _ from 'lodash'
  12. export default {
  13. name: 'Affix',
  14. props: {
  15. /**
  16. * 距离窗口顶部达到指定偏移量后触发
  17. */
  18. offsetTop: {
  19. type: Number,
  20. required: false,
  21. default: () => null
  22. },
  23. offset: {
  24. type: Number,
  25. required: false,
  26. default: () => null
  27. },
  28. /** 距离窗口底部达到指定偏移量后触发 */
  29. offsetBottom: {
  30. type: Number,
  31. required: false,
  32. default: () => null
  33. },
  34. /** 固定状态改变时触发的回调函数 */
  35. // onChange?: (_Affixed?: boolean) => void;
  36. /** 设置 _Affix 需要监听其滚动事件的元素,值为一个返回对应 DOM 元素的函数 */
  37. target: {
  38. type: Function,
  39. required: false,
  40. default: () => getDefaultTarget()
  41. },
  42. prefixCls: {
  43. type: String,
  44. required: false,
  45. default: () => null
  46. }
  47. },
  48. data() {
  49. this.lazyUpdatePosition = _.throttle(this.lazyUpdatePosition, 300)
  50. return {
  51. placeholderNodeStyle: '',
  52. fixedNodeStyle: '',
  53. entity: {
  54. target: null,
  55. affixList: [],
  56. eventHandlers: {}
  57. }
  58. }
  59. },
  60. mounted() {
  61. // console.log(this.target().Target)
  62. this.init()
  63. },
  64. // 销毁事件
  65. destroyed() {
  66. this.unInit()
  67. },
  68. methods: {
  69. init() {
  70. const { entity } = this
  71. const targetNode = this.target()
  72. TRIGGER_EVENTS.forEach((eventName) => {
  73. // targetNode.addEventListener(eventName, () => {
  74. // this.lazyUpdatePosition()
  75. // })
  76. entity.eventHandlers[eventName] = addDOMEventListener(
  77. targetNode,
  78. eventName,
  79. () => {
  80. this.lazyUpdatePosition()
  81. }
  82. )
  83. })
  84. this.lazyUpdatePosition()
  85. },
  86. // @ts-ignore TS6133
  87. prepareMeasure() {
  88. this.fixedNodeStyle = null
  89. this.placeholderNodeStyle = null
  90. this.$forceUpdate()
  91. },
  92. getOffsetTop() {
  93. const { offset, offsetBottom } = this
  94. let { offsetTop } = this
  95. if (typeof offsetTop === 'undefined') {
  96. offsetTop = offset
  97. }
  98. if (offsetBottom === undefined && offsetTop === undefined) {
  99. offsetTop = 0
  100. }
  101. return offsetTop
  102. },
  103. lazyUpdatePosition() {
  104. const targetNode = this.target()
  105. const offsetTop = this.getOffsetTop()
  106. // console.log(102, this.$refs, targetNode.scrollTop)
  107. if (targetNode.scrollTop < offsetTop) {
  108. this.prepareMeasure()
  109. return
  110. }
  111. const placeholderReact = this.$refs.placeholderNode
  112. this.fixedNodeStyle = {
  113. position: 'fixed',
  114. top: `${offsetTop}px`,
  115. zIndex: 999,
  116. width: placeholderReact.offsetWidth + 'px',
  117. height: placeholderReact.offsetHeight + 'px'
  118. }
  119. this.placeholderNodeStyle = {
  120. width: placeholderReact.offsetWidth + 'px',
  121. height: placeholderReact.offsetHeight + 'px'
  122. }
  123. },
  124. unInit() {
  125. const { entity } = this
  126. // const targetNode = this.target()
  127. // console.log(123, targetNode)
  128. TRIGGER_EVENTS.forEach((eventName) => {
  129. // targetNode.removeEventListener(eventName, () => {
  130. // this.lazyUpdatePosition()
  131. // })
  132. // entity.eventHandlers[eventName]
  133. const handler = entity.eventHandlers[eventName]
  134. if (handler && handler.remove) {
  135. handler.remove()
  136. }
  137. })
  138. }
  139. }
  140. }
  141. </script>
  142. <style scoped lang="less"></style>