You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
209 lines
6.0 KiB
209 lines
6.0 KiB
|
3 years ago
|
<template>
|
||
|
|
<view :id="$sUid" :class="custom_class" :style="wrap_style">
|
||
|
|
<view class="s-sticky" :class="c_class" :style="c_style">
|
||
|
|
<slot />
|
||
|
|
</view>
|
||
|
|
</view>
|
||
|
|
</template>
|
||
|
|
|
||
|
|
<script>
|
||
|
|
import componentMixin from '../../mixins/componentMixin';
|
||
|
|
|
||
|
|
/**
|
||
|
|
* s-sticky 粘性布局
|
||
|
|
* @description Sticky 组件与 CSS 中position: sticky属性实现的效果一致,当组件在屏幕范围内时,会按照正常的布局排列,当组件滚出屏幕范围时,始终会固定在屏幕顶部
|
||
|
|
* @property {Number} scrollTop 通过传入的scrollTop改变来触发scroll事件
|
||
|
|
* @property {String} position = [top|bottom] 吸顶方向
|
||
|
|
* @property {Number|String} offsetTop 吸顶时与顶部的距离,单位rpx
|
||
|
|
* @property {Number|String} offsetBottom 吸底时与底部的距离,单位rpx
|
||
|
|
* @property {Boolean} enable 启用粘性功能
|
||
|
|
* @property {Number|String} zIndex z-index,在吸附时的z-index
|
||
|
|
* @property {Function} container 一个函数,返回容器对应的 NodesRef 节点
|
||
|
|
* @event {Function} change (bool) 吸附状态改变事件
|
||
|
|
* @example <s-sticky></s-sticky>
|
||
|
|
*/
|
||
|
|
export default {
|
||
|
|
name: 'SSticky',
|
||
|
|
mixins: [componentMixin],
|
||
|
|
props: {
|
||
|
|
scrollTop: {
|
||
|
|
type: Number,
|
||
|
|
default: 0,
|
||
|
|
},
|
||
|
|
position: {
|
||
|
|
type: String,
|
||
|
|
default: 'top',
|
||
|
|
},
|
||
|
|
offsetTop: {
|
||
|
|
type: [Number, String],
|
||
|
|
default: 0,
|
||
|
|
},
|
||
|
|
offsetBottom: {
|
||
|
|
type: [Number, String],
|
||
|
|
default: 0,
|
||
|
|
},
|
||
|
|
enable: {
|
||
|
|
type: Boolean,
|
||
|
|
default: true,
|
||
|
|
},
|
||
|
|
zIndex: [Number, String],
|
||
|
|
container: Function,
|
||
|
|
},
|
||
|
|
data: () => ({
|
||
|
|
sticky: false,
|
||
|
|
fixed: false,
|
||
|
|
width: 0,
|
||
|
|
height: 0,
|
||
|
|
transform: 0,
|
||
|
|
windowHeight: 0,
|
||
|
|
windowTop: 0,
|
||
|
|
windowBottom: 0,
|
||
|
|
}),
|
||
|
|
computed: {
|
||
|
|
wrap_style() {
|
||
|
|
return this.enable && this.fixed ? this.$mergeStyle({
|
||
|
|
height: this.$addUnit(this.height, 'px'),
|
||
|
|
}) : '';
|
||
|
|
},
|
||
|
|
c_class() {
|
||
|
|
return this.$mergeClass({
|
||
|
|
's-sticky--fixed': this.fixed,
|
||
|
|
's-sticky--sticky': this.sticky,
|
||
|
|
}, this.custom_class);
|
||
|
|
},
|
||
|
|
c_style() {
|
||
|
|
const style = {};
|
||
|
|
if (this.enable) {
|
||
|
|
if (this.fixed) {
|
||
|
|
style[this.position] = this.$addUnit(this.offset + this.other_height, 'px');
|
||
|
|
style.width = this.$addUnit(this.width, 'px');
|
||
|
|
}
|
||
|
|
if (this.sticky) {
|
||
|
|
style.zIndex = this.zIndex;
|
||
|
|
}
|
||
|
|
if (this.transform) {
|
||
|
|
style.transform = 'translate3d(0, ' + this.transform + 'px, 0)';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return this.$mergeStyle(
|
||
|
|
style,
|
||
|
|
this.custom_style,
|
||
|
|
);
|
||
|
|
},
|
||
|
|
offset() {
|
||
|
|
return this.$toPx(this.position === 'top' ? this.offsetTop : this.offsetBottom);
|
||
|
|
},
|
||
|
|
other_height() {
|
||
|
|
return parseInt(this.position === 'top' ? this.windowTop : this.windowBottom);
|
||
|
|
},
|
||
|
|
scrollState() {
|
||
|
|
return [
|
||
|
|
this.enable,
|
||
|
|
this.scrollTop,
|
||
|
|
this.position,
|
||
|
|
this.container,
|
||
|
|
this.offsetTop,
|
||
|
|
this.offsetBottom,
|
||
|
|
];
|
||
|
|
},
|
||
|
|
},
|
||
|
|
watch: {
|
||
|
|
enable(bool) {
|
||
|
|
if (!bool) this.resetStickyData();
|
||
|
|
},
|
||
|
|
scrollState() {
|
||
|
|
this.onScroll();
|
||
|
|
},
|
||
|
|
sticky(bool) {
|
||
|
|
this.$emit('change', bool);
|
||
|
|
},
|
||
|
|
},
|
||
|
|
created() {
|
||
|
|
const systemInfo = uni.getSystemInfoSync();
|
||
|
|
this.windowHeight = systemInfo.windowHeight;
|
||
|
|
this.windowTop = systemInfo.windowTop || 0;
|
||
|
|
this.windowBottom = systemInfo.windowBottom || 0;
|
||
|
|
},
|
||
|
|
mounted() {
|
||
|
|
this.$nextTick(this.onScroll);
|
||
|
|
},
|
||
|
|
methods: {
|
||
|
|
getRootRect() {
|
||
|
|
return this.$getRect(`#${this.$sUid}`);
|
||
|
|
},
|
||
|
|
getContainerRect(container) {
|
||
|
|
return new Promise(resolve => container().boundingClientRect(resolve).exec());
|
||
|
|
},
|
||
|
|
clearTimer() {
|
||
|
|
if (this.timer) {
|
||
|
|
clearTimeout(this.timer);
|
||
|
|
this.timer = null;
|
||
|
|
}
|
||
|
|
},
|
||
|
|
onScroll() {
|
||
|
|
this.clearTimer();
|
||
|
|
this.timer = setTimeout(() => {
|
||
|
|
this.timer = null;
|
||
|
|
const { _isMounted, _isDestroyed, enable, position, offset, windowHeight } = this;
|
||
|
|
if (!_isMounted || _isDestroyed || !enable) return;
|
||
|
|
const container = this.$getPropsFn('container');
|
||
|
|
if (position === 'top') {
|
||
|
|
if (container) {
|
||
|
|
Promise.all([this.getRootRect(), this.getContainerRect(container)]).then(([rootRect, containerRect]) => {
|
||
|
|
this.resetStickyData();
|
||
|
|
if (offset + rootRect.height >= containerRect.height + containerRect.top) {
|
||
|
|
this.transform = containerRect.height - rootRect.height;
|
||
|
|
this.sticky = true;
|
||
|
|
} else if (offset >= rootRect.top && containerRect.bottom >= 0) {
|
||
|
|
this.setStickyData(rootRect);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
this.getRootRect().then(rootRect => {
|
||
|
|
this.resetStickyData();
|
||
|
|
if (offset >= rootRect.top) {
|
||
|
|
this.setStickyData(rootRect);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if (container) {
|
||
|
|
Promise.all([this.getRootRect(), this.getContainerRect(container)]).then(([rootRect, containerRect]) => {
|
||
|
|
this.resetStickyData();
|
||
|
|
if (offset + rootRect.height >= windowHeight - containerRect.top) {
|
||
|
|
this.transform = rootRect.height - containerRect.height;
|
||
|
|
this.sticky = true;
|
||
|
|
} else if (offset >= windowHeight - rootRect.bottom && windowHeight >= containerRect.top) {
|
||
|
|
this.setStickyData(rootRect);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
this.getRootRect().then(rootRect => {
|
||
|
|
this.resetStickyData();
|
||
|
|
if (offset >= windowHeight - rootRect.bottom) {
|
||
|
|
this.setStickyData(rootRect);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
},
|
||
|
|
resetStickyData() {
|
||
|
|
this.sticky = false;
|
||
|
|
this.fixed = false;
|
||
|
|
this.width = 0;
|
||
|
|
this.height = 0;
|
||
|
|
this.transform = 0;
|
||
|
|
},
|
||
|
|
setStickyData(rect) {
|
||
|
|
this.sticky = true;
|
||
|
|
this.fixed = true;
|
||
|
|
this.width = rect.width;
|
||
|
|
this.height = rect.height;
|
||
|
|
},
|
||
|
|
},
|
||
|
|
};
|
||
|
|
</script>
|
||
|
|
|
||
|
|
<style lang="scss" src="./index.scss"></style>
|