diff --git a/packages/mixins/page-scroll.ts b/packages/mixins/page-scroll.ts
new file mode 100644
index 00000000..06715b1a
--- /dev/null
+++ b/packages/mixins/page-scroll.ts
@@ -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
+ );
+ }
+ });
diff --git a/packages/sticky/index.ts b/packages/sticky/index.ts
index d9b43cea..2235a452 100644
--- a/packages/sticky/index.ts
+++ b/packages/sticky/index.ts
@@ -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();
}
});
diff --git a/packages/sticky/index.wxml b/packages/sticky/index.wxml
index aa17a795..15e9f4a8 100644
--- a/packages/sticky/index.wxml
+++ b/packages/sticky/index.wxml
@@ -2,7 +2,7 @@
-
+
diff --git a/packages/sticky/index.wxs b/packages/sticky/index.wxs
index faa9570f..18efe147 100644
--- a/packages/sticky/index.wxs
+++ b/packages/sticky/index.wxs
@@ -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 = {