美文网首页
Affix Vue 3 TypeScript版本

Affix Vue 3 TypeScript版本

作者: EasyNetCN | 来源:发表于2021-03-23 16:29 被阅读0次
<template>
    <div ref="root">
        <div ref="point" :class="classes" :style="styles">
            <slot></slot>
        </div>
        <div v-show="slot" :style="slotStyle"></div>
    </div>
</template>
<script lang="ts">
import { computed, defineComponent, nextTick, onBeforeUnmount, onMounted, ref, toRefs } from 'vue'
import { on, off } from '../../utils/dom'
const prefixCls = 'ivu-affix'

function getScroll (target: Window, top = false) {
  const prop = top ? 'pageYOffset' : 'pageXOffset'
  const method = top ? 'scrollTop' : 'scrollLeft'

  let ret = target[prop]

  if (typeof ret !== 'number') {
    ret = window.document.documentElement[method]
  }

  return ret
}

function getOffset (element: HTMLElement) {
  const rect = element.getBoundingClientRect()

  const scrollTop = getScroll(window, true)
  const scrollLeft = getScroll(window)

  const docEl = window.document.body
  const clientTop = docEl.clientTop || 0
  const clientLeft = docEl.clientLeft || 0

  return {
    top: rect.top + scrollTop - clientTop,
    left: rect.left + scrollLeft - clientLeft
  }
}

export default defineComponent({
  name: 'Affix',
  props: {
    offsetTop: {
      type: Number,
      default: 0
    },
    offsetBottom: {
      type: Number
    },
    useCapture: {
      type: Boolean,
      default: false
    }
  },
  emits: ['on-change'],
  setup (props, ctx) {
    const { offsetTop, offsetBottom, useCapture } = toRefs(props)
    const root = ref(null)
    const point = ref(null)
    const affix = ref(false)
    const styles = ref({})
    const slot = ref(false)
    const slotStyle = ref({})

    const offsetType = computed(() => {
      let type = 'top'
      if (offsetBottom?.value !== undefined && offsetBottom.value >= 0) {
        type = 'bottom'
      }

      return type
    })

    const classes = computed(() => {
      return [
        {
          [`${prefixCls}`]: affix.value
        }
      ]
    })

    const handleScroll = function () {
      const scrollTop = getScroll(window, true)
      const elOffset = getOffset(root.value!)
      const windowHeight = window.innerHeight
      const elHeight = (root.value! as HTMLElement).getElementsByTagName('div')[0].offsetHeight

      // Fixed Top
      if ((elOffset.top - offsetTop.value) < scrollTop && offsetType.value === 'top' && !affix.value) {
        affix.value = true
        slotStyle.value = {
          width: (point.value! as HTMLElement).clientWidth + 'px',
          height: (point.value! as HTMLElement).clientHeight + 'px'
        }
        slot.value = true
        styles.value = {
          top: `${offsetTop.value}px`,
          left: `${elOffset.left}px`,
          width: `${(root.value! as HTMLElement).offsetWidth}px`
        }

        ctx.emit('on-change', true)
      } else if ((elOffset.top - offsetTop.value) > scrollTop && offsetType.value === 'top' && affix) {
        slot.value = false
        slotStyle.value = {}
        affix.value = false
        styles.value = {}

        ctx.emit('on-change', false)
      }

      // Fixed Bottom
      if ((offsetBottom && offsetBottom.value && (elOffset.top + offsetBottom.value + elHeight) > (scrollTop + windowHeight)) && offsetType.value === 'bottom' && !affix.value) {
        affix.value = true
        styles.value = {
          bottom: `${offsetBottom?.value}px`,
          left: `${elOffset.left}px`,
          width: `${(root.value! as HTMLElement).offsetWidth}px`
        }

        ctx.emit('on-change', true)
      } else if (((offsetBottom && offsetBottom.value && (elOffset.top + offsetBottom.value + elHeight) < (scrollTop + windowHeight)) && offsetType.value === 'bottom' && affix)) {
        affix.value = false
        styles.value = {}

        ctx.emit('on-change', false)
      }
    }

    onMounted(() => {
      on(window, 'scroll', handleScroll, useCapture.value)
      on(window, 'resize', handleScroll, useCapture.value)

      console.log(root.value)
      nextTick(() => {
        handleScroll()
      })
    })

    onBeforeUnmount(() => {
      off(window, 'scroll', handleScroll, useCapture.value)
      off(window, 'resize', handleScroll, useCapture.value)
    })

    return {
      affix,
      styles,
      slot,
      slotStyle,
      offsetType,
      classes,
      root,
      point
    }
  }
})
</script>

相关文章

网友评论

      本文标题:Affix Vue 3 TypeScript版本

      本文链接:https://www.haomeiwen.com/subject/mmvfhltx.html