From 623bea55fdb060ac1e35af0edae7bac6d6e16b5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=88=E5=85=89=E5=80=BE=E5=9F=8E?= Date: Fri, 6 Sep 2019 14:18:16 +0800 Subject: [PATCH] =?UTF-8?q?[new=20feature]:=20=E6=96=B0=E5=A2=9E=E7=BB=84?= =?UTF-8?q?=E4=BB=B6sticky=20(#1954)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- example/app.json | 2 + example/config.js | 4 + example/pages/sticky/index.js | 5 ++ example/pages/sticky/index.json | 3 + example/pages/sticky/index.wxml | 15 ++++ example/pages/sticky/index.wxss | 14 ++++ packages/sticky/README.md | 46 +++++++++++ packages/sticky/index.json | 3 + packages/sticky/index.less | 3 + packages/sticky/index.ts | 131 ++++++++++++++++++++++++++++++++ packages/sticky/index.wxml | 5 ++ 11 files changed, 231 insertions(+) create mode 100644 example/pages/sticky/index.js create mode 100644 example/pages/sticky/index.json create mode 100644 example/pages/sticky/index.wxml create mode 100644 example/pages/sticky/index.wxss create mode 100644 packages/sticky/README.md create mode 100644 packages/sticky/index.json create mode 100644 packages/sticky/index.less create mode 100644 packages/sticky/index.ts create mode 100644 packages/sticky/index.wxml diff --git a/example/app.json b/example/app.json index 592b2aba..7ed69795 100644 --- a/example/app.json +++ b/example/app.json @@ -32,6 +32,7 @@ "pages/progress/index", "pages/stepper/index", "pages/steps/index", + "pages/sticky/index", "pages/switch/index", "pages/search/index", "pages/slider/index", @@ -94,6 +95,7 @@ "van-slider": "./dist/slider/index", "van-stepper": "./dist/stepper/index", "van-steps": "./dist/steps/index", + "van-sticky": "./dist/sticky/index", "van-submit-bar": "./dist/submit-bar/index", "van-swipe-cell": "./dist/swipe-cell/index", "van-switch": "./dist/switch/index", diff --git a/example/config.js b/example/config.js index b11d1da4..dcada1c1 100644 --- a/example/config.js +++ b/example/config.js @@ -129,6 +129,10 @@ export default [ path: '/steps', title: 'Steps 步骤条' }, + { + path: '/sticky', + title: 'Sticky 粘性布局' + }, { path: '/tag', title: 'Tag 标记' diff --git a/example/pages/sticky/index.js b/example/pages/sticky/index.js new file mode 100644 index 00000000..c54a95ec --- /dev/null +++ b/example/pages/sticky/index.js @@ -0,0 +1,5 @@ +import Page from '../../common/page'; + +Page({ + data: {} +}); diff --git a/example/pages/sticky/index.json b/example/pages/sticky/index.json new file mode 100644 index 00000000..8a847a49 --- /dev/null +++ b/example/pages/sticky/index.json @@ -0,0 +1,3 @@ +{ + "navigationBarTitleText": "Sticky 粘性布局" +} diff --git a/example/pages/sticky/index.wxml b/example/pages/sticky/index.wxml new file mode 100644 index 00000000..747f3406 --- /dev/null +++ b/example/pages/sticky/index.wxml @@ -0,0 +1,15 @@ + + + + 基础用法 + + + + + + + + 吸顶距离 + + + diff --git a/example/pages/sticky/index.wxss b/example/pages/sticky/index.wxss new file mode 100644 index 00000000..f88f1fa4 --- /dev/null +++ b/example/pages/sticky/index.wxss @@ -0,0 +1,14 @@ +page { + height: 200vh; +} + +.van-button { + margin-left: 16px; +} + +.sticky-container { + position: relative; + z-index: -1; + height: 150px; + background-color: #fff; +} diff --git a/packages/sticky/README.md b/packages/sticky/README.md new file mode 100644 index 00000000..b33d17e6 --- /dev/null +++ b/packages/sticky/README.md @@ -0,0 +1,46 @@ +# Sticky 粘性布局 + +### 引入 +在`app.json`或`index.json`中引入组件,默认为`ES6`版本,`ES5`引入方式参见[快速上手](#/quickstart) + +```json +"usingComponents": { + "van-sticky": "path/to/vant-weapp/dist/sticky/index" +} +``` + + +## 代码演示 + +### 基础用法 + +将内容包裹在`Sticky`组件内即可 + +```html + + 基础用法 + +``` + +### 吸顶距离 + +通过`offset-top`属性可以设置组件在吸顶时与顶部的距离 + +```html + + 吸顶距离 + +``` + +### Props + +| 参数 | 说明 | 类型 | 默认值 | +|-----------|-----------|-----------|-------------| +| offset-top | 吸顶时与顶部的距离,单位`px` | *number* | `0` | +| z-index | 吸顶时的 z-index | *number* | `99` | + +### Events + +| 事件名 | 说明 | 回调参数 | +|-----------|-----------|-----------| +| scroll | 滚动时触发 | { scrollTop: 距离顶部位置, isFixed: 是否吸顶 } | diff --git a/packages/sticky/index.json b/packages/sticky/index.json new file mode 100644 index 00000000..467ce294 --- /dev/null +++ b/packages/sticky/index.json @@ -0,0 +1,3 @@ +{ + "component": true +} diff --git a/packages/sticky/index.less b/packages/sticky/index.less new file mode 100644 index 00000000..9159955c --- /dev/null +++ b/packages/sticky/index.less @@ -0,0 +1,3 @@ +.van-sticky { + position: relative; +} diff --git a/packages/sticky/index.ts b/packages/sticky/index.ts new file mode 100644 index 00000000..058a3710 --- /dev/null +++ b/packages/sticky/index.ts @@ -0,0 +1,131 @@ +import { VantComponent } from '../common/component'; +import { nextTick } from '../common/utils'; + +type Position = 'top' | 'bottom' | ''; + +VantComponent({ + props: { + zIndex: { + type: Number, + value: 1 + }, + offsetTop: { + type: Number, + value: 0 + } + }, + + data: { + position: '', // 当前定位 + height: 0, + wrapStyle: '', + containerStyle: '' + }, + + methods: { + setWrapStyle() { + const { offsetTop, position } = this.data as { + offsetTop: number + position: Position + }; + let wrapStyle: string; + let containerStyle: string; + + switch (position) { + case 'top': + wrapStyle = ` + top: ${offsetTop}px; + position: fixed; + `; + containerStyle = `height: ${this.itemHeight}px;`; + break; + case 'bottom': + wrapStyle = ` + top: auto; + bottom: 0; + `; + containerStyle = ''; + break; + default: + wrapStyle = ''; + containerStyle = ''; + } + + // cut down `set`x + let data: any = {}; + if (wrapStyle !== this.data.wrapStyle) data.wrapStyle = wrapStyle; + if (containerStyle !== this.data.containerStyle) data.containerStyle = containerStyle; + if (JSON.stringify(data) !== '{}') this.set(data); + }, + + setPosition(position: Position) { + if (position !== this.data.position) { + this.set({ position }); + nextTick(() => { + this.setWrapStyle(); + }); + } + }, + + observerContentScroll() { + const { offsetTop = 0 } = this.data; + const { windowHeight } = wx.getSystemInfoSync(); + + this.createIntersectionObserver({}).disconnect(); + + // @ts-ignore + this.createIntersectionObserver() + .relativeToViewport({ top: - (this.itemHeight + offsetTop) }) + .observe('.van-sticky', (res: WechatMiniprogram.ObserveCallbackResult) => { + const { top } = res.boundingClientRect; + + if (top > offsetTop) { + return; + } + + const position: Position = 'top'; + + this.$emit('scroll', { + scrollTop: top + offsetTop, + isFixed: true + }); + + this.setPosition(position); + }); + + // @ts-ignore + this.createIntersectionObserver() + .relativeToViewport({ bottom: -(windowHeight - 1 - offsetTop) }) + .observe('.van-sticky', (res: WechatMiniprogram.ObserveCallbackResult) => { + const { top, bottom } = res.boundingClientRect; + + if (bottom <= this.itemHeight - 1) { + return; + } + + const position: Position = res.intersectionRatio > 0 ? 'top' : ''; + + this.$emit('scroll', { + scrollTop: top + offsetTop, + isFixed: position === 'top' + }); + + this.setPosition(position); + }); + } + }, + + mounted() { + this.getRect('.van-sticky').then( + (rect: WechatMiniprogram.BoundingClientRectCallbackResult) => { + this.itemHeight = rect.height; + this.itemTop = rect.top; + this.observerContentScroll(); + } + ); + }, + + destroyed() { + this.createIntersectionObserver({}).disconnect(); + } +}); diff --git a/packages/sticky/index.wxml b/packages/sticky/index.wxml new file mode 100644 index 00000000..73360c28 --- /dev/null +++ b/packages/sticky/index.wxml @@ -0,0 +1,5 @@ + + + + +