mirror of
https://gitee.com/vant-contrib/vant-weapp.git
synced 2025-04-05 10:22:44 +08:00
refactor(Sticky): refactor component with page scroll (#2950)
fix #2651, fix #2418
This commit is contained in:
parent
cb85269f17
commit
26fd387ee8
42
packages/mixins/page-scroll.ts
Normal file
42
packages/mixins/page-scroll.ts
Normal file
@ -0,0 +1,42 @@
|
||||
type IPageScrollOption = WechatMiniprogram.Page.IPageScrollOption;
|
||||
type Scroller = (event: IPageScrollOption) => void;
|
||||
type TrivialInstance = WechatMiniprogram.Page.TrivialInstance & {
|
||||
vanPageScroller?: Scroller[];
|
||||
};
|
||||
|
||||
function getCurrentPage(): TrivialInstance {
|
||||
const pages = getCurrentPages();
|
||||
return pages[pages.length - 1] || ({} as TrivialInstance);
|
||||
}
|
||||
|
||||
function onPageScroll(event: IPageScrollOption) {
|
||||
const { vanPageScroller = [] } = getCurrentPage();
|
||||
|
||||
vanPageScroller.forEach((scroller: Scroller) => {
|
||||
if (typeof scroller === 'function') {
|
||||
scroller(event);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const pageScrollMixin = (scroller: Scroller) =>
|
||||
Behavior({
|
||||
attached() {
|
||||
const page = getCurrentPage();
|
||||
|
||||
if (Array.isArray(page.vanPageScroller)) {
|
||||
page.vanPageScroller.push(scroller.bind(this));
|
||||
} else {
|
||||
page.vanPageScroller = [page.onPageScroll, scroller.bind(this)];
|
||||
}
|
||||
|
||||
page.onPageScroll = onPageScroll;
|
||||
},
|
||||
|
||||
detached() {
|
||||
const page = getCurrentPage();
|
||||
page.vanPageScroller = (page.vanPageScroller || []).filter(
|
||||
item => item !== scroller
|
||||
);
|
||||
}
|
||||
});
|
@ -1,7 +1,10 @@
|
||||
import { VantComponent } from '../common/component';
|
||||
import { pageScrollMixin } from '../mixins/page-scroll';
|
||||
|
||||
const ROOT_ELEMENT = '.van-sticky';
|
||||
|
||||
type BoundingClientRect = WechatMiniprogram.BoundingClientRectCallbackResult;
|
||||
|
||||
VantComponent({
|
||||
props: {
|
||||
zIndex: {
|
||||
@ -11,163 +14,106 @@ VantComponent({
|
||||
offsetTop: {
|
||||
type: Number,
|
||||
value: 0,
|
||||
observer: 'observeContent'
|
||||
observer: 'onScroll'
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
observer(value) {
|
||||
if (!this.mounted) {
|
||||
return;
|
||||
}
|
||||
value ? this.disconnectObserver() : this.initObserver();
|
||||
}
|
||||
observer: 'onScroll'
|
||||
},
|
||||
container: {
|
||||
type: null,
|
||||
observer(target: () => WechatMiniprogram.NodesRef) {
|
||||
if (typeof target !== 'function' || !this.data.height) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.observeContainer();
|
||||
this.updateFixed();
|
||||
}
|
||||
observer: 'onScroll'
|
||||
}
|
||||
},
|
||||
|
||||
mixins: [
|
||||
pageScrollMixin(function(event) {
|
||||
this.onScroll(event);
|
||||
})
|
||||
],
|
||||
|
||||
data: {
|
||||
height: 0,
|
||||
fixed: false
|
||||
fixed: false,
|
||||
transform: 0
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.onScroll();
|
||||
},
|
||||
|
||||
methods: {
|
||||
onScroll({ scrollTop } = {}) {
|
||||
const { container, offsetTop, disabled } = this.data;
|
||||
|
||||
if (disabled) {
|
||||
this.setDataAfterDiff({
|
||||
fixed: false,
|
||||
transform: 0
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this.scrollTop = scrollTop || this.scrollTop;
|
||||
|
||||
if (typeof container === 'function') {
|
||||
Promise.all([this.getRect(ROOT_ELEMENT), this.getContainerRect()]).then(
|
||||
([root, container]: BoundingClientRect[]) => {
|
||||
if (offsetTop + root.height > container.height + container.top) {
|
||||
this.setDataAfterDiff({
|
||||
fixed: false,
|
||||
transform: container.height - root.height
|
||||
});
|
||||
} else if (offsetTop >= root.top) {
|
||||
this.setDataAfterDiff({
|
||||
fixed: true,
|
||||
height: root.height,
|
||||
transform: 0
|
||||
});
|
||||
} else {
|
||||
this.setDataAfterDiff({ fixed: false, transform: 0 });
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.getRect(ROOT_ELEMENT).then((root: BoundingClientRect) => {
|
||||
if (offsetTop >= root.top) {
|
||||
this.setDataAfterDiff({ fixed: true, height: root.height });
|
||||
this.transform = 0;
|
||||
} else {
|
||||
this.setDataAfterDiff({ fixed: false });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setDataAfterDiff(data) {
|
||||
wx.nextTick(() => {
|
||||
const diff = Object.keys(data).reduce((prev, key) => {
|
||||
if (data[key] !== this.data[key]) {
|
||||
prev[key] = data[key];
|
||||
}
|
||||
|
||||
return prev;
|
||||
}, {});
|
||||
|
||||
this.setData(diff);
|
||||
|
||||
this.$emit('scroll', {
|
||||
scrollTop: this.scrollTop,
|
||||
isFixed: data.fixed || this.data.fixed
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
getContainerRect() {
|
||||
const nodesRef: WechatMiniprogram.NodesRef = this.data.container();
|
||||
|
||||
return new Promise(resolve =>
|
||||
nodesRef.boundingClientRect(resolve).exec()
|
||||
);
|
||||
},
|
||||
|
||||
initObserver() {
|
||||
this.disconnectObserver();
|
||||
|
||||
this.getRect(ROOT_ELEMENT).then(
|
||||
(rect: WechatMiniprogram.BoundingClientRectCallbackResult) => {
|
||||
this.setData({ height: rect.height });
|
||||
|
||||
wx.nextTick(() => {
|
||||
this.observeContent();
|
||||
this.observeContainer();
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
updateFixed() {
|
||||
Promise.all([this.getRect(ROOT_ELEMENT), this.getContainerRect()]).then(
|
||||
([
|
||||
content,
|
||||
container
|
||||
]: WechatMiniprogram.BoundingClientRectCallbackResult[]) => {
|
||||
this.setData({ height: content.height });
|
||||
this.containerHeight = container.height;
|
||||
|
||||
wx.nextTick(() => {
|
||||
this.setFixed(content.top);
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
disconnectObserver(observerName?: string) {
|
||||
if (observerName) {
|
||||
const observer: WechatMiniprogram.IntersectionObserver = this[
|
||||
observerName
|
||||
];
|
||||
observer && observer.disconnect();
|
||||
} else {
|
||||
this.contentObserver && this.contentObserver.disconnect();
|
||||
this.containerObserver && this.containerObserver.disconnect();
|
||||
}
|
||||
},
|
||||
|
||||
observeContent() {
|
||||
const { offsetTop } = this.data;
|
||||
|
||||
this.disconnectObserver('contentObserver');
|
||||
const contentObserver = this.createIntersectionObserver({
|
||||
thresholds: [0.9, 1]
|
||||
});
|
||||
contentObserver.relativeToViewport({ top: -offsetTop });
|
||||
contentObserver.observe(ROOT_ELEMENT, res => {
|
||||
if (this.data.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setFixed(res.boundingClientRect.top);
|
||||
});
|
||||
|
||||
this.contentObserver = contentObserver;
|
||||
},
|
||||
|
||||
observeContainer() {
|
||||
if (typeof this.data.container !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
const { height } = this.data;
|
||||
|
||||
this.getContainerRect().then(
|
||||
(rect: WechatMiniprogram.BoundingClientRectCallbackResult) => {
|
||||
this.containerHeight = rect.height;
|
||||
|
||||
this.disconnectObserver('containerObserver');
|
||||
const containerObserver = this.createIntersectionObserver({
|
||||
thresholds: [0.9, 1]
|
||||
});
|
||||
this.containerObserver = containerObserver;
|
||||
containerObserver.relativeToViewport({
|
||||
top: this.containerHeight - height
|
||||
});
|
||||
containerObserver.observe(ROOT_ELEMENT, res => {
|
||||
if (this.data.disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setFixed(res.boundingClientRect.top);
|
||||
});
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
setFixed(top) {
|
||||
const { offsetTop, height } = this.data;
|
||||
const { containerHeight } = this;
|
||||
|
||||
const fixed =
|
||||
containerHeight && height
|
||||
? top >= height - containerHeight && top < offsetTop
|
||||
: top < offsetTop;
|
||||
|
||||
this.$emit('scroll', {
|
||||
scrollTop: top,
|
||||
isFixed: fixed
|
||||
});
|
||||
|
||||
this.setData({ fixed });
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.mounted = true;
|
||||
|
||||
if (!this.data.disabled) {
|
||||
this.initObserver();
|
||||
}
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
this.disconnectObserver();
|
||||
}
|
||||
});
|
||||
|
@ -2,7 +2,7 @@
|
||||
<wxs src="./index.wxs" module="computed" />
|
||||
|
||||
<view class="custom-class van-sticky" style="{{ computed.containerStyle({ fixed, height, zIndex }) }}">
|
||||
<view class="{{ utils.bem('sticky-wrap', { fixed }) }}" style="{{ computed.wrapStyle({ fixed, offsetTop }) }}">
|
||||
<view class="{{ utils.bem('sticky-wrap', { fixed }) }}" style="{{ computed.wrapStyle({ fixed, offsetTop, transform, zIndex }) }}">
|
||||
<slot />
|
||||
</view>
|
||||
</view>
|
||||
|
@ -1,18 +1,34 @@
|
||||
/* eslint-disable */
|
||||
function wrapStyle(data) {
|
||||
if (data.fixed) {
|
||||
return 'top: ' + data.offsetTop + 'px;';
|
||||
var style = '';
|
||||
|
||||
if (data.transform) {
|
||||
style += 'transform: translate3d(0, ' + data.transform + 'px, 0);';
|
||||
}
|
||||
|
||||
return '';
|
||||
if (data.fixed) {
|
||||
style += 'top: ' + data.offsetTop + 'px;';
|
||||
}
|
||||
|
||||
if (data.zIndex) {
|
||||
style += 'z-index: ' + data.zIndex + ';';
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
function containerStyle(data) {
|
||||
var style = '';
|
||||
|
||||
if (data.fixed) {
|
||||
return 'height: ' + data.height + 'px; z-index: ' + data.zIndex + ';';
|
||||
style += 'height: ' + data.height + 'px;';
|
||||
}
|
||||
|
||||
return '';
|
||||
if (data.zIndex) {
|
||||
style += 'z-index: ' + data.zIndex + ';';
|
||||
}
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
Loading…
x
Reference in New Issue
Block a user