¶Ô±ÈÐÂÎļþ |
| | |
| | | <template> |
| | | <view |
| | | class="u-sticky" |
| | | :id="elId" |
| | | :style="[style]" |
| | | > |
| | | <view |
| | | :style="[stickyContent]" |
| | | class="u-sticky__content" |
| | | > |
| | | <slot /> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import props from './props.js';; |
| | | /** |
| | | * sticky å¸é¡¶ |
| | | * @description 该ç»ä»¶ä¸CSSä¸position: sticky屿§å®ç°çææä¸è´ï¼å½ç»ä»¶è¾¾å°é¢è®¾çå°é¡¶é¨è·ç¦»æ¶ï¼ å°±ä¼åºå®å¨æå®ä½ç½®ï¼ç»ä»¶ä½ç½®å¤§äºé¢è®¾çé¡¶é¨è·ç¦»æ¶ï¼ä¼éæ°æç
§æ£å¸¸çå¸å±æåã |
| | | * @tutorial https://www.uviewui.com/components/sticky.html |
| | | * @property {String ï½ Number} offsetTop å¸é¡¶æ¶ä¸é¡¶é¨çè·ç¦»ï¼åä½pxï¼é»è®¤ 0 ï¼ |
| | | * @property {String ï½ Number} customNavHeight èªå®ä¹å¯¼èªæ çé«åº¦ ï¼h5 é»è®¤44 å
¶ä»é»è®¤ 0 ï¼ |
| | | * @property {Boolean} disabled æ¯å¦å¼å¯å¸é¡¶åè½ ï¼é»è®¤ false ï¼ |
| | | * @property {String} bgColor ç»ä»¶èæ¯é¢è²ï¼é»è®¤ '#ffffff' ï¼ |
| | | * @property {String ï½ Number} zIndex å¸é¡¶æ¶çz-indexå¼ |
| | | * @property {String ï½ Number} index èªå®ä¹æ è¯ï¼ç¨äºåºåæ¯åªä¸ä¸ªç»ä»¶ |
| | | * @property {Object} customStyle ç»ä»¶çæ ·å¼ï¼å¯¹è±¡å½¢å¼ |
| | | * @event {Function} fixed ç»ä»¶å¸é¡¶æ¶è§¦å |
| | | * @event {Function} unfixed ç»ä»¶åæ¶å¸é¡¶æ¶è§¦å |
| | | * @example <u-sticky offsetTop="200"><view>å¡ä¸ç§æ¥é£æ¯å¼ï¼è¡¡é³é廿 çæ</view></u-sticky> |
| | | */ |
| | | export default { |
| | | name: 'u-sticky', |
| | | mixins: [uni.$u.mpMixin, uni.$u.mixin, props], |
| | | data() { |
| | | return { |
| | | cssSticky: false, // æ¯å¦ä½¿ç¨cssçstickyå®ç° |
| | | stickyTop: 0, // å¸é¡¶çtopå¼ï¼å 为å¯è½åèªå®ä¹å¯¼èªæ å½±åï¼æç»çå¸é¡¶å¼éoffsetTopå¼ |
| | | elId: uni.$u.guid(), |
| | | left: 0, // jsæ¨¡å¼æ¶ï¼å¸é¡¶çå
容å 为å¤äºpostition: fixed模å¼ï¼ä¸ºäºååæ¥ä¿æä¸è´çæ ·å¼ï¼éè¦è®°å½å¹¶éæ°è®¾ç½®å®çleftï¼heightï¼width屿§ |
| | | width: 'auto', |
| | | height: 'auto', |
| | | fixed: false, // jsæ¨¡å¼æ¶ï¼æ¯å¦å¤äºå¸é¡¶æ¨¡å¼ |
| | | } |
| | | }, |
| | | computed: { |
| | | style() { |
| | | const style = {} |
| | | if(!this.disabled) { |
| | | if (this.cssSticky) { |
| | | style.position = 'sticky' |
| | | style.zIndex = this.uZindex |
| | | style.top = uni.$u.addUnit(this.stickyTop) |
| | | } else { |
| | | style.height = this.fixed ? this.height + 'px' : 'auto' |
| | | } |
| | | } else { |
| | | // æ éå¸é¡¶æ¶ï¼è®¾ç½®ä¼é»è®¤çrelative(nvue)åénvueçstaticéææ¨¡å¼å³å¯ |
| | | // #ifdef APP-NVUE |
| | | style.position = 'relative' |
| | | // #endif |
| | | // #ifndef APP-NVUE |
| | | style.position = 'static' |
| | | // #endif |
| | | } |
| | | style.backgroundColor = this.bgColor |
| | | return uni.$u.deepMerge(uni.$u.addStyle(this.customStyle), style) |
| | | }, |
| | | // å¸é¡¶å
å®¹çæ ·å¼ |
| | | stickyContent() { |
| | | const style = {} |
| | | if (!this.cssSticky) { |
| | | style.position = this.fixed ? 'fixed' : 'static' |
| | | style.top = this.stickyTop + 'px' |
| | | style.left = this.left + 'px' |
| | | style.width = this.width == 'auto' ? 'auto' : this.width + 'px' |
| | | style.zIndex = this.uZindex |
| | | } |
| | | return style |
| | | }, |
| | | uZindex() { |
| | | return this.zIndex ? this.zIndex : uni.$u.zIndex.sticky |
| | | } |
| | | }, |
| | | mounted() { |
| | | this.init() |
| | | }, |
| | | methods: { |
| | | init() { |
| | | this.getStickyTop() |
| | | // å¤æä½¿ç¨çæ¨¡å¼ |
| | | this.checkSupportCssSticky() |
| | | // 妿䏿¯æcss stickyï¼å使ç¨jsæ¹æ¡ï¼æ¤æ¹æ¡æ§è½æ¯ä¸ä¸cssæ¹æ¡ |
| | | if (!this.cssSticky) { |
| | | !this.disabled && this.initObserveContent() |
| | | } |
| | | }, |
| | | initObserveContent() { |
| | | // è·åå¸é¡¶å
容çé«åº¦ï¼ç¨äºå¨jså¸é¡¶æ¨¡å¼æ¶ï¼ç»ç¶å
ç´ ä¸ä¸ªå¡«å
é«åº¦ï¼é²æ¢"å¡é·" |
| | | this.$uGetRect('#' + this.elId).then((res) => { |
| | | this.height = res.height |
| | | this.left = res.left |
| | | this.width = res.width |
| | | this.$nextTick(() => { |
| | | this.observeContent() |
| | | }) |
| | | }) |
| | | }, |
| | | observeContent() { |
| | | // å
ææä¹åçè§å¯ |
| | | this.disconnectObserver('contentObserver') |
| | | const contentObserver = uni.createIntersectionObserver({ |
| | | // æ£æµçåºé´èå´ |
| | | thresholds: [0.95, 0.98, 1] |
| | | }) |
| | | // å°å±å¹é¡¶é¨çé«åº¦æ¶è§¦å |
| | | contentObserver.relativeToViewport({ |
| | | top: -this.stickyTop |
| | | }) |
| | | // ç»å®è§å¯çå
ç´ |
| | | contentObserver.observe(`#${this.elId}`, res => { |
| | | this.setFixed(res.boundingClientRect.top) |
| | | }) |
| | | this.contentObserver = contentObserver |
| | | }, |
| | | setFixed(top) { |
| | | // 夿æ¯å¦åºäºå¸é¡¶æ¡ä»¶èå´ |
| | | const fixed = top <= this.stickyTop |
| | | this.fixed = fixed |
| | | }, |
| | | disconnectObserver(observerName) { |
| | | // ææè§å¯ï¼éæ¾èµæº |
| | | const observer = this[observerName] |
| | | observer && observer.disconnect() |
| | | }, |
| | | getStickyTop() { |
| | | this.stickyTop = uni.$u.getPx(this.offsetTop) + uni.$u.getPx(this.customNavHeight) |
| | | }, |
| | | async checkSupportCssSticky() { |
| | | // #ifdef H5 |
| | | // H5ï¼ä¸è¬é½æ¯ç°ä»£æµè§å¨ï¼æ¯æ¯æcss stickyçï¼è¿é使ç¨å建å
ç´ å
æ¢çå½¢å¼å¤æ |
| | | if (this.checkCssStickyForH5()) { |
| | | this.cssSticky = true |
| | | } |
| | | // #endif |
| | | |
| | | // 妿å®åçæ¬é«äº8.0ï¼ä¾ç¶è®¤ä¸ºæ¯æ¯æcss stickyç(å 为å®å7å¨æäºæºåï¼å¯è½ä¸æ¯æsticky) |
| | | if (uni.$u.os() === 'android' && Number(uni.$u.sys().system) > 8) { |
| | | this.cssSticky = true |
| | | } |
| | | |
| | | // APP-Vueå微信平å°ï¼éè¿computedStyle夿æ¯å¦æ¯æcss sticky |
| | | // #ifdef APP-VUE || MP-WEIXIN |
| | | this.cssSticky = await this.checkComputedStyle() |
| | | // #endif |
| | | |
| | | // iosä¸ï¼ä»ios6å¼å§ï¼é½æ¯æ¯æcss stickyç |
| | | if (uni.$u.os() === 'ios') { |
| | | this.cssSticky = true |
| | | } |
| | | |
| | | // nvueï¼æ¯æ¯æcss stickyç |
| | | // #ifdef APP-NVUE |
| | | this.cssSticky = true |
| | | // #endif |
| | | }, |
| | | // å¨APPå微信å°ç¨åºä¸ï¼éè¿uni.createSelectorQueryå¯ä»¥å¤ææ¯å¦æ¯æcss sticky |
| | | checkComputedStyle() { |
| | | // æ¹æ³å
è¿è¡å¤æï¼é¿å
å¨å
¶ä»å¹³å°çææ ç¨ä»£ç |
| | | // #ifdef APP-VUE || MP-WEIXIN |
| | | return new Promise(resolve => { |
| | | uni.createSelectorQuery().in(this).select('.u-sticky').fields({ |
| | | computedStyle: ["position"] |
| | | }).exec(e => { |
| | | resolve('sticky' === e[0].position) |
| | | }) |
| | | }) |
| | | // #endif |
| | | }, |
| | | // H5éè¿å建å
ç´ çå½¢å¼å
æ¢æ¯å¦æ¯æcss sticky |
| | | // 夿æµè§å¨æ¯å¦æ¯æsticky屿§ |
| | | checkCssStickyForH5() { |
| | | // æ¹æ³å
è¿è¡å¤æï¼é¿å
å¨å
¶ä»å¹³å°çææ ç¨ä»£ç |
| | | // #ifdef H5 |
| | | const vendorList = ['', '-webkit-', '-ms-', '-moz-', '-o-'], |
| | | vendorListLength = vendorList.length, |
| | | stickyElement = document.createElement('div') |
| | | for (let i = 0; i < vendorListLength; i++) { |
| | | stickyElement.style.position = vendorList[i] + 'sticky' |
| | | if (stickyElement.style.position !== '') { |
| | | return true |
| | | } |
| | | } |
| | | return false; |
| | | // #endif |
| | | } |
| | | }, |
| | | beforeDestroy() { |
| | | this.disconnectObserver('contentObserver') |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .u-sticky { |
| | | /* #ifdef APP-VUE || MP-WEIXIN */ |
| | | // æ¤å¤é»è®¤åsticky屿§ï¼æ¯ä¸ºäºç»å¾®ä¿¡åAPPéè¿uni.createSelectorQueryæ¥è¯¢æ¯å¦æ¯æcss stickyä½¿ç¨ |
| | | position: sticky; |
| | | /* #endif */ |
| | | } |
| | | </style> |