blob: d9bc34c073bc54b54a92ecc00a34c1b6f4bd6a9e [file] [log] [blame]
guangchao.xu070005a2020-12-07 09:56:40 +08001<template>
2 <view class="">
3 <view class="u-sticky-wrap" :class="[elClass]" :style="{
4 height: fixed ? height + 'px' : 'auto',
5 backgroundColor: bgColor
6 }">
7 <view class="u-sticky" :style="{
8 position: fixed ? 'fixed' : 'static',
9 top: stickyTop + 'px',
10 left: left + 'px',
11 width: width == 'auto' ? 'auto' : width + 'px',
12 zIndex: uZIndex
13 }">
14 <slot></slot>
15 </view>
16 </view>
17 </view>
18</template>
19
20<script>
21 /**
22 * sticky 吸顶
23 * @description 该组件与CSS中position: sticky属性实现的效果一致,当组件达到预设的到顶部距离时, 就会固定在指定位置,组件位置大于预设的顶部距离时,会重新按照正常的布局排列。
24 * @tutorial https://www.uviewui.com/components/sticky.html
25 * @property {String Number} offset-top 吸顶时与顶部的距离,单位rpx(默认0)
26 * @property {String Number} index 自定义标识,用于区分是哪一个组件
27 * @property {Boolean} enable 是否开启吸顶功能(默认true)
28 * @property {String} bg-color 组件背景颜色(默认#ffffff)
29 * @property {String Number} z-index 吸顶时的z-index值(默认970)
30 * @property {String Number} h5-nav-height 导航栏高度,自定义导航栏时(无导航栏时需设置为0),需要传入此值,单位px(默认44)
31 * @event {Function} fixed 组件吸顶时触发
32 * @event {Function} unfixed 组件取消吸顶时触发
33 * @example <u-sticky offset-top="200"><view>塞下秋来风景异,衡阳雁去无留意</view></u-sticky>
34 */
35 export default {
36 name: "u-sticky",
37 props: {
38 // 吸顶容器到顶部某个距离的时候,进行吸顶,在H5平台,NavigationBar为44px
39 offsetTop: {
40 type: [Number, String],
41 default: 0
42 },
43 //列表中的索引值
44 index: {
45 type: [Number, String],
46 default: ''
47 },
48 // 是否开启吸顶功能
49 enable: {
50 type: Boolean,
51 default: true
52 },
53 // h5顶部导航栏的高度
54 h5NavHeight: {
55 type: [Number, String],
56 default: 44
57 },
58 // 吸顶区域的背景颜色
59 bgColor: {
60 type: String,
61 default: '#ffffff'
62 },
63 // z-index值
64 zIndex: {
65 type: [Number, String],
66 default: ''
67 }
68 },
69 data() {
70 return {
71 fixed: false,
72 height: 'auto',
73 stickyTop: 0,
74 elClass: this.$u.guid(),
75 left: 0,
76 width: 'auto',
77 };
78 },
79 watch: {
80 offsetTop(val) {
81 this.initObserver();
82 },
83 enable(val) {
84 if (val == false) {
85 this.fixed = false;
86 this.disconnectObserver('contentObserver');
87 } else {
88 this.initObserver();
89 }
90 }
91 },
92 computed: {
93 uZIndex() {
94 return this.zIndex ? this.zIndex : this.$u.zIndex.sticky;
95 }
96 },
97 mounted() {
98 this.initObserver();
99 },
100 methods: {
101 initObserver() {
102 if (!this.enable) return;
103 // #ifdef H5
104 this.stickyTop = this.offsetTop != 0 ? uni.upx2px(this.offsetTop) + this.h5NavHeight : this.h5NavHeight;
105 // #endif
106 // #ifndef H5
107 this.stickyTop = this.offsetTop != 0 ? uni.upx2px(this.offsetTop) : 0;
108 // #endif
109
110 this.disconnectObserver('contentObserver');
111 this.$uGetRect('.' + this.elClass).then((res) => {
112 this.height = res.height;
113 this.left = res.left;
114 this.width = res.width;
115 this.$nextTick(() => {
116 this.observeContent();
117 });
118 });
119 },
120 observeContent() {
121 this.disconnectObserver('contentObserver');
122 const contentObserver = this.createIntersectionObserver({
123 thresholds: [0.95, 0.98, 1]
124 });
125 contentObserver.relativeToViewport({
126 top: -this.stickyTop
127 });
128 contentObserver.observe('.' + this.elClass, res => {
129 if (!this.enable) return;
130 this.setFixed(res.boundingClientRect.top);
131 });
132 this.contentObserver = contentObserver;
133 },
134 setFixed(top) {
135 const fixed = top < this.stickyTop;
136 if (fixed) this.$emit('fixed', this.index);
137 else if(this.fixed) this.$emit('unfixed', this.index);
138 this.fixed = fixed;
139 },
140 disconnectObserver(observerName) {
141 const observer = this[observerName];
142 observer && observer.disconnect();
143 },
144 },
145 beforeDestroy() {
146 this.disconnectObserver('contentObserver');
147 }
148 };
149</script>
150
151<style scoped lang="scss">
152 @import "../../libs/css/style.components.scss";
153
154 .u-sticky {
155 z-index: 9999999999;
156 }
157</style>