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