fix: fix bug

This commit is contained in:
huanghong 2022-04-02 14:58:18 +08:00
parent 46133b3ff3
commit 179ca064ba
1 changed files with 41 additions and 34 deletions

View File

@ -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>