11<template >
22 <view style =" position : relative " >
33 <view :class =" `wd-sticky-box ${props.customClass}`" :style =" customStyle" :id =" styckyBoxId" >
4- <wd-resize @resize =" resizeHandler " >
4+ <wd-resize @resize =" handleResize " >
55 <slot />
66 </wd-resize >
77 </view >
@@ -20,38 +20,43 @@ export default {
2020 </script >
2121
2222<script lang="ts" setup>
23- import { getCurrentInstance , onBeforeMount , provide , ref } from ' vue'
23+ import { getCurrentInstance , onBeforeMount , reactive , ref } from ' vue'
2424import { getRect , uuid } from ' ../common/util'
2525import { baseProps } from ' ../common/props'
26+ import { STICKY_BOX_KEY } from ' ./types'
27+ import { useChildren } from ' ../composables/useChildren'
2628
2729const props = defineProps (baseProps )
2830
2931const styckyBoxId = ref <string >(` wd-sticky-box${uuid ()} ` )
3032
3133const observerMap = ref <Map <any , any >>(new Map ())
32- const height = ref <number >(0 )
33- const width = ref <number >(0 )
3434
35- const stickyList: any [] = [] // 子元素sticky列表
35+ const boxStyle = reactive ({
36+ height: 0 ,
37+ width: 0
38+ })
3639
3740const { proxy } = getCurrentInstance () as any
38- const instance = getCurrentInstance () as any
3941
40- provide (' box-height' , height )
41- provide (' box-width' , width )
42- provide (' observerForChild' , observerForChild )
42+ const { children : stickyList, linkChildren } = useChildren (STICKY_BOX_KEY )
43+ linkChildren ({
44+ boxStyle: boxStyle ,
45+ observerForChild
46+ })
4347
4448onBeforeMount (() => {
4549 observerMap .value = new Map ()
4650})
4751
4852/**
49- * @description wd-sticky-box 尺寸发生变化时,重新监听所有的viewport
53+ * 容器大小变化后重新监听sticky组件与box组件的交叉状态
54+ * @param detail
5055 */
51- function resizeHandler (detail : any ) {
56+ function handleResize (detail : any ) {
5257 // 相对的容器大小改变后,同步设置 wd-sticky-box 的大小
53- width . value = detail .width
54- height . value = detail .height
58+ boxStyle . width = detail .width
59+ boxStyle . height = detail .height
5560 // wd-sticky-box 大小变化时,重新监听所有吸顶元素
5661 const temp = observerMap .value
5762 observerMap .value = new Map ()
@@ -67,8 +72,8 @@ function resizeHandler(detail: any) {
6772 temp .clear ()
6873}
6974/**
70- * @description 删除 wd-sticky 废弃的监听器
71- * @param child
75+ * 删除对指定sticky的监听
76+ * @param child 指定的子组件
7277 */
7378function deleteObserver(child : any ) {
7479 const observer = observerMap .value .get (child .$ .uid )
@@ -77,63 +82,70 @@ function deleteObserver(child: any) {
7782 observerMap .value .delete (child .$ .uid )
7883}
7984/**
80- * @description 为 wd-sticky 创建监听器
81- * @param child
85+ * 针对指定sticky添加监听
86+ * @param child 指定的子组件
8287 */
8388function createObserver(child : any ) {
84- const observer = uni .createIntersectionObserver (instance )
89+ const observer = uni .createIntersectionObserver (proxy , { thresholds: [ 0 , 0.5 ] } )
8590 observerMap .value .set (child .$ .uid , observer )
8691 return observer
8792}
8893/**
89- * @description 为单个 wd-sticky 监听 viewport
90- * @param child sticky
94+ * 监听子组件
95+ * @param child 子组件
9196 */
9297function observerForChild(child : any ) {
93- const hasChild = stickyList .find ((sticky ) => {
94- return sticky .$ .uid === child .$ .uid
95- })
96- if (! hasChild ) {
97- stickyList .push (child )
98- }
9998 deleteObserver (child )
10099 const observer = createObserver (child )
101-
102100 const exposed = child .$ .exposed
103- const offset = exposed .height .value + exposed .offsetTop
101+ let offset = exposed .stickyState .height + exposed .offsetTop
102+ // #ifdef H5
103+ // H5端,导航栏为普通元素,需要将组件移动到导航栏的下边沿
104+ // H5的导航栏高度为44px
105+ offset = offset + 44
106+ // #endif
104107
105- // 如果 wd-sticky 比 wd-sticky-box还大,"相对吸顶"无任何意义,此时强制吸顶元素回归其占位符
106- if (height .value <= exposed .height .value ) {
108+ if (boxStyle .height <= exposed .stickyState .height ) {
107109 exposed .setPosition (false , ' absolute' , 0 )
108110 }
109111 observer .relativeToViewport ({ top: - offset }).observe (` #${styckyBoxId .value } ` , (result ) => {
110- scrollHandler (exposed , result )
112+ handleRelativeTo (exposed , result )
111113 })
114+ // 当子组件默认处于边界外且永远不会进入边界内时,需要手动调用一次
112115 getRect (` #${styckyBoxId .value } ` , false , proxy )
113116 .then ((res ) => {
114- // 当 wd-sticky-box 位于 viewport 外部时不会触发 observe,此时根据位置手动修复位置。
115- if (Number (res .bottom ) <= offset ) scrollHandler (exposed , { boundingClientRect: res })
117+ // #ifdef H5
118+ // H5端,查询节点信息未计算导航栏高度
119+ res .bottom = Number (res .bottom ) + 44
120+ // #endif
121+ if (Number (res .bottom ) <= offset ) handleRelativeTo (exposed , { boundingClientRect: res })
116122 })
117123 .catch ((res ) => {
118124 console .log (res )
119125 })
120126}
121127/**
122- * @description 为子节点监听 viewport,处理子节点的相对吸顶逻辑
128+ * 监听容器组件
123129 * @param {Object} exposed wd-sticky实例暴露出的事件
124- * @param {Object} boundingClientRect 目标节点各个边在viewport中的坐标
130+ * @param {Object} boundingClientRect 边界信息
125131 */
126- function scrollHandler(exposed : any , { boundingClientRect }: any ) {
127- const offset = exposed .height .value + exposed .offsetTop
128- if (boundingClientRect .bottom <= offset ) {
129- // 父元素即将被吸顶元素遮盖,将吸顶元素固定到父元素底部
130- exposed .setPosition (true , ' absolute' , boundingClientRect .height - exposed .height .value )
131- } else if (boundingClientRect .top <= offset && boundingClientRect .bottom > offset ) {
132- // wd-sticky 已经完全呈现了 viewport 中了,
133- // 此时没有必要再相对 wd-sticky-box 吸顶了
134- if (exposed .state .value === ' normal' ) return
135- // 顶元素开始遮盖不住父元素了,将吸顶元素恢复到吸顶模式
136- exposed .setPosition (false , ' fixed' , exposed .offsetTop )
132+ function handleRelativeTo(exposed : any , { boundingClientRect }: any ) {
133+ let childOffsetTop = exposed .offsetTop
134+ // #ifdef H5
135+ // H5端,导航栏为普通元素,需要将组件移动到导航栏的下边沿
136+ // H5的导航栏高度为44px
137+ childOffsetTop = childOffsetTop + 44
138+ // #endif
139+ const offset = exposed .stickyState .height + childOffsetTop
140+ let isAbsolute = boundingClientRect .bottom <= offset
141+ // #ifdef H5 || APP-PLUS
142+ isAbsolute = boundingClientRect .bottom < offset
143+ // #endif
144+ if (isAbsolute ) {
145+ exposed .setPosition (true , ' absolute' , boundingClientRect .height - exposed .stickyState .height )
146+ } else if (boundingClientRect .top <= offset && ! isAbsolute ) {
147+ if (exposed .stickyState .state === ' normal' ) return
148+ exposed .setPosition (false , ' fixed' , childOffsetTop )
137149 }
138150}
139151 </script >
0 commit comments