huanghong 3 роки тому
батько
коміт
179ca064ba
1 змінених файлів з 41 додано та 34 видалено
  1. 41 34
      src/components/Sticky/src/Sticky.vue

+ 41 - 34
src/components/Sticky/src/Sticky.vue

@@ -1,7 +1,8 @@
 <script setup lang="ts">
 import { propTypes } from '@/utils/propTypes'
 import { ref, onMounted, onActivated, shallowRef } from 'vue'
-import { useEventListener, useWindowSize } from '@vueuse/core'
+import { useEventListener, useWindowSize, isClient } from '@vueuse/core'
+import type { CSSProperties } from 'vue'
 const props = defineProps({
   // 距离顶部或者底部的距离(单位px)
   offset: propTypes.number.def(0),
@@ -19,16 +20,16 @@ const props = defineProps({
   }
 })
 const active = ref(false)
-const positionStyle = ref('' as any)
-const width = ref(undefined as any)
-const height = ref(undefined as any)
+const width = ref('auto' as string)
+const height = ref('auto' as string)
 const isSticky = ref(false)
-const refSticky = ref()
-const scrollContainer = shallowRef()
+const refSticky = shallowRef<HTMLElement>()
+const scrollContainer = shallowRef<HTMLElement | Window>()
 const { height: windowHeight } = useWindowSize()
 onMounted(() => {
-  height.value = refSticky.value.getBoundingClientRect().height
-  scrollContainer.value = getScrollContainer(refSticky.value, true)
+  height.value = refSticky.value?.getBoundingClientRect().height + 'px'
+
+  scrollContainer.value = getScrollContainer(refSticky.value!, true)
   useEventListener(scrollContainer, 'scroll', handleScroll)
   useEventListener('resize', handleReize)
   handleScroll()
@@ -37,55 +38,64 @@ onActivated(() => {
   handleScroll()
 })
 
-const camelize = (str) => {
+const camelize = (str: string): string => {
   return str.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : ''))
 }
 
-const getStyle = (element, styleName) => {
-  let _a
+const getStyle = (element: HTMLElement, styleName: keyof CSSProperties): string => {
+  if (!isClient || !element || !styleName) return ''
+
   let key = camelize(styleName)
   if (key === 'float') key = 'cssFloat'
   try {
     const style = element.style[styleName]
     if (style) return style
-    const computed = (_a = document.defaultView) == null ? void 0 : _a.getComputedStyle(element, '')
+    const computed = document.defaultView?.getComputedStyle(element, '')
     return computed ? computed[styleName] : ''
-  } catch (e) {
+  } catch {
     return element.style[styleName]
   }
 }
-
-const isScroll = (el, isVertical) => {
-  const key = {
-    undefined: 'overflow',
-    true: 'overflow-y',
-    false: 'overflow-x'
-  }[String(isVertical)]
+const isScroll = (el: HTMLElement, isVertical?: boolean): boolean => {
+  if (!isClient) return false
+  const key = (
+    {
+      undefined: 'overflow',
+      true: 'overflow-y',
+      false: 'overflow-x'
+    } as const
+  )[String(isVertical)]!
   const overflow = getStyle(el, key)
   return ['scroll', 'auto', 'overlay'].some((s) => overflow.includes(s))
 }
-const getScrollContainer = (el, isVertical) => {
+
+const getScrollContainer = (
+  el: HTMLElement,
+  isVertical: boolean
+): Window | HTMLElement | undefined => {
+  if (!isClient) return
   let parent = el
   while (parent) {
     if ([window, document, document.documentElement].includes(parent)) return window
     if (isScroll(parent, isVertical)) return parent
-    parent = parent.parentNode
+    parent = parent.parentNode as HTMLElement
   }
   return parent
 }
 
 const handleScroll = () => {
-  width.value = refSticky.value.getBoundingClientRect().width
+  width.value = refSticky.value!.getBoundingClientRect().width! + 'px'
   if (props.position === 'top') {
-    const offsetTop = refSticky.value.getBoundingClientRect().top
-    if (offsetTop < props.offset) {
+    const offsetTop = refSticky.value?.getBoundingClientRect().top
+    if (offsetTop !== undefined && offsetTop < props.offset) {
       sticky()
       return
     }
     reset()
   } else {
-    const offsetBottom = refSticky.value.getBoundingClientRect().bottom
-    if (offsetBottom > windowHeight.value - props.offset) {
+    const offsetBottom = refSticky.value?.getBoundingClientRect().bottom
+
+    if (offsetBottom !== undefined && offsetBottom > windowHeight.value - props.offset) {
       sticky()
       return
     }
@@ -93,7 +103,7 @@ const handleScroll = () => {
   }
 }
 const handleReize = () => {
-  if (isSticky.value) {
+  if (isSticky.value && refSticky.value) {
     width.value = refSticky.value.getBoundingClientRect().width + 'px'
   }
 }
@@ -101,32 +111,29 @@ const sticky = () => {
   if (active.value) {
     return
   }
-  positionStyle.value = 'fixed'
   active.value = true
-  width.value = width.value + 'px'
   isSticky.value = true
 }
 const reset = () => {
   if (!active.value) {
     return
   }
-  positionStyle.value = ''
   width.value = 'auto'
   active.value = false
   isSticky.value = false
 }
 </script>
 <template>
-  <div :style="{ height: height + 'px', zIndex: zIndex }" ref="refSticky">
+  <div :style="{ height: height, zIndex: zIndex }" ref="refSticky">
     <div
       :class="className"
       :style="{
         top: position === 'top' ? offset + 'px' : '',
         bottom: position !== 'top' ? offset + 'px' : '',
         zIndex: zIndex,
-        position: positionStyle,
+        position: active ? 'fixed' : 'static',
         width: width,
-        height: height + 'px'
+        height: height
       }"
     >
       <slot>