From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目
---
src/components/Sticky/src/Sticky.vue | 143 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 143 insertions(+), 0 deletions(-)
diff --git a/src/components/Sticky/src/Sticky.vue b/src/components/Sticky/src/Sticky.vue
new file mode 100644
index 0000000..28ecbcb
--- /dev/null
+++ b/src/components/Sticky/src/Sticky.vue
@@ -0,0 +1,143 @@
+<script lang="ts" setup>
+import { propTypes } from '@/utils/propTypes'
+import { isClient, useEventListener, useWindowSize } from '@vueuse/core'
+import type { CSSProperties } from 'vue'
+
+defineOptions({ name: 'Sticky' })
+
+const props = defineProps({
+ // 璺濈椤堕儴鎴栬�呭簳閮ㄧ殑璺濈(鍗曚綅px)
+ offset: propTypes.number.def(0),
+ // 璁剧疆鍏冪礌鐨勫爢鍙犻『搴�
+ zIndex: propTypes.number.def(999),
+ // 璁剧疆鎸囧畾鐨刢lass
+ className: propTypes.string.def(''),
+ // 瀹氫綅鏂瑰紡锛岄粯璁や负(top)锛岃〃绀鸿窛绂婚《閮ㄤ綅缃紝鍙互璁剧疆涓簍op鎴栬�卋ottom
+ position: {
+ type: String,
+ validator: function (value: string) {
+ return ['top', 'bottom'].indexOf(value) !== -1
+ },
+ default: 'top'
+ }
+})
+const width = ref('auto' as string)
+const height = ref('auto' as string)
+const isSticky = ref(false)
+const refSticky = shallowRef<HTMLElement>()
+const scrollContainer = shallowRef<HTMLElement | Window>()
+const { height: windowHeight } = useWindowSize()
+onMounted(() => {
+ height.value = refSticky.value?.getBoundingClientRect().height + 'px'
+
+ scrollContainer.value = getScrollContainer(refSticky.value!, true)
+ useEventListener(scrollContainer, 'scroll', handleScroll)
+ useEventListener('resize', handleResize)
+ handleScroll()
+})
+onActivated(() => {
+ handleScroll()
+})
+
+const camelize = (str: string): string => {
+ return str.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : ''))
+}
+
+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 = document.defaultView?.getComputedStyle(element, '')
+ return computed ? computed[styleName] : ''
+ } catch {
+ return element.style[styleName]
+ }
+}
+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: 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 as HTMLElement
+ }
+ return parent
+}
+
+const handleScroll = () => {
+ width.value = refSticky.value!.getBoundingClientRect().width! + 'px'
+ if (props.position === 'top') {
+ const offsetTop = refSticky.value?.getBoundingClientRect().top
+ if (offsetTop !== undefined && offsetTop < props.offset) {
+ sticky()
+ return
+ }
+ reset()
+ } else {
+ const offsetBottom = refSticky.value?.getBoundingClientRect().bottom
+
+ if (offsetBottom !== undefined && offsetBottom > windowHeight.value - props.offset) {
+ sticky()
+ return
+ }
+ reset()
+ }
+}
+const handleResize = () => {
+ if (isSticky.value && refSticky.value) {
+ width.value = refSticky.value.getBoundingClientRect().width + 'px'
+ }
+}
+const sticky = () => {
+ if (isSticky.value) {
+ return
+ }
+ isSticky.value = true
+}
+const reset = () => {
+ if (!isSticky.value) {
+ return
+ }
+ width.value = 'auto'
+ isSticky.value = false
+}
+</script>
+<template>
+ <div ref="refSticky" :style="{ height: height, zIndex: zIndex }">
+ <div
+ :class="className"
+ :style="{
+ top: position === 'top' ? offset + 'px' : '',
+ bottom: position !== 'top' ? offset + 'px' : '',
+ zIndex: zIndex,
+ position: isSticky ? 'fixed' : 'static',
+ width: width,
+ height: height
+ }"
+ >
+ <slot>
+ <div>sticky</div>
+ </slot>
+ </div>
+ </div>
+</template>
--
Gitblit v1.8.0