提交 80b31809 编写于 作者: inkwalk's avatar inkwalk

add: uni.createMediaQueryObserver Api and mediaQueryObserver Object

上级 7e31ef7d
节点布局交叉状态 API 可用于监听两个或多个组件节点在布局位置上的相交状态。这一组API常常可以用于推断某些节点是否可以被用户看见、有多大比例可以被用户看见。
### uni.createIntersectionObserver([this], [options])
创建并返回一个 ``IntersectionObserver`` 对象实例。
**this说明:**
自定义组件实例。**支付宝小程序不支持此参数,传入仅为抹平写法差异**
**options 的可选参数为:**
|字段名|类型|说明|
|:-|:-|:-|
|thresholds|Array|一个数值数组,包含所有阈值。默认为 ``[0]``。|
|initialRatio|Number|初始的相交比例,如果调用时检测到的相交比例与这个值不相等且达到阈值,则会触发一次监听器的回调函数。默认为 ``0``。|
|observeAll|Boolean|是否同时观测多个参照节点(而非一个),如果设为 ``true````observe````targetSelector`` 将选中多个节点(注意:同时选中过多节点将影响渲染性能)|
### IntersectionObserver 对象的方法列表
|方法|说明|
|:-|:-|
|IntersectionObserver.relativeTo(selector,[margins])|使用选择器指定一个节点,作为参照区域之一。|
|IntersectionObserver.relativeToViewport([margins])|指定页面显示区域作为参照区域之一|
|IntersectionObserver.observe(selector,[callback])|指定目标节点并开始监听相交状态变化情况。回调函数 ``callback`` 包含一个参数 ``result``|
|IntersectionObserver.disconnect()|停止监听。回调函数将不再触发。|
**margins 参数:** 用来扩展(或收缩)参照节点布局区域的边界。
|属性|类型|默认值|是否必填|说明|
|:-|:-|:-|:-|:-|
|left|number||否|节点布局区域的左边界|
|right|number||否|节点布局区域的右边界|
|top|number||否|节点布局区域的上边界|
|bottom|number||否|节点布局区域的下边界|
下面的示例代码中,如果目标节点 ``".test"`` 进入 ``".scroll"`` 区域以下 100px 时,就会触发回调函数。
```
uni.createIntersectionObserver(this).relativeTo('.scroll',{bottom: 100}).observe('.test', (res) => {
console.log(res);
})
```
**observe 回调函数 result 包含的字段**
|字段名|类型|说明|
|:-|:-|:-|
|intersectionRatio|Number|相交比例|
|intersectionRect|Object|相交区域的边界,包含 ``left````right````top````bottom`` 四项|
|boundingClientRect|Object|目标节点布局区域的边界,包含 ``left````right````top````bottom`` 四项|
|relativeRect|Object|参照区域的边界,包含 ``left````right````top````bottom`` 四项|
|time|Number|相交检测时的时间戳|
**Tips**
- 与页面显示区域的相交区域并不准确代表用户可见的区域,因为参与计算的区域是“布局区域”,布局区域可能会在绘制时被其他节点裁剪隐藏(如祖先节点中 overflow 样式为 hidden 的节点)或遮盖(如 fixed 定位的节点)。
- 节点交互状态 ``API`` 建议在 ``onReady`` 生命周期里监听,因为此 ``API`` 需要查找页面元素,``onReady`` 时页面已经完成初次渲染,已经能查找到对应的元素。
### 代码示例
```
<template>
<view class="container">
<text>{{appear ? '小球出现' : '小球消失'}}</text>
<view class="page-section">
<scroll-view class="scroll-view" scroll-y>
<view class="scroll-area">
<text class="notice">向下滚动让小球出现</text>
<view class="ball"></view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
let observer = null;
export default {
data() {
return {
appear: false
}
},
onReady() {
observer = uni.createIntersectionObserver(this);
observer.relativeTo('.scroll-view').observe('.ball', (res) => {
if (res.intersectionRatio > 0 && !this.appear) {
this.appear = true;
} else if (!res.intersectionRatio > 0 && this.appear) {
this.appear = false;
}
})
},
onUnload() {
if (observer) {
observer.disconnect()
}
}
}
</script>
<style>
view,page {
display: flex;
flex-direction: column;
}
.scroll-view {
height: 400rpx;
background: #fff;
border: 1px solid #ccc;
box-sizing: border-box;
}
.scroll-area {
height: 1300rpx;
display: flex;
flex-direction: column;
align-items: center;
transition: .5s;
}
.notice {
margin-top: 150rpx;
margin: 150rpx 0 400rpx 0;
}
.ball {
width: 200rpx;
height: 200rpx;
background: #1AAD19;
border-radius: 50%;
}
</style>
```
......@@ -161,6 +161,7 @@ const ui = [
'stopPullDownRefresh',
'createSelectorQuery',
'createIntersectionObserver',
'createMediaQueryObserver',
'getMenuButtonBoundingClientRect'
]
......
......@@ -113,15 +113,15 @@
"uni.getBLEDeviceServices": true,
"uni.getBLEDeviceCharacteristics": true,
"uni.createBLEConnection": true,
"uni.closeBLEConnection": true,
"uni.setBLEMTU": true,
"uni.closeBLEConnection": true,
"uni.setBLEMTU": true,
"uni.getBLEDeviceRSSI": true,
"uni.onBeaconServiceChange": true,
"uni.onBeaconUpdate": true,
"uni.getBeacons": true,
"uni.startBeaconDiscovery": true,
"uni.stopBeaconDiscovery": true,
"uni.onThemeChange": true,
"uni.stopBeaconDiscovery": true,
"uni.onThemeChange": true,
"uni.onUIStyleChange": true
}
}, {
......@@ -157,7 +157,8 @@
"uni.stopPullDownRefresh": true,
"uni.createSelectorQuery": true,
"uni.createIntersectionObserver": true,
"uni.hideKeyboard": true,
"uni.createMediaQueryObserver": true,
"uni.hideKeyboard": true,
"uni.onKeyboardHeightChange": true
}
}, {
......
import createCallbacks from 'uni-helpers/callbacks'
import {
getCurrentPageVm
} from '../../platform'
const createMediaQueryObserverCallbacks = createCallbacks('requestMediaQueryObserver')
class ServiceMediaQueryObserver {
constructor (component, options) {
this.pageId = component.$page.id
this.component = component._$id || component // app-plus 平台传输_$id
this.options = options
}
observe (options, callback) {
if (typeof callback !== 'function') {
return
}
this.options = options
this.reqId = createMediaQueryObserverCallbacks.push(callback)
UniServiceJSBridge.publishHandler('requestMediaQueryObserver', {
reqId: this.reqId,
component: this.component,
options: this.options
}, this.pageId)
}
disconnect () {
UniServiceJSBridge.publishHandler('destroyMediaQueryObserver', {
reqId: this.reqId
}, this.pageId)
}
}
export function createMediaQueryObserver (context, options) {
if (!context._isVue) {
options = context
context = null
}
if (context) {
return new ServiceMediaQueryObserver(context, options)
}
return new ServiceMediaQueryObserver(getCurrentPageVm('createMediaQueryObserver'), options)
}
......@@ -50,6 +50,23 @@ export default function initSubscribe (subscribe, {
}
}
const requestMediaQueryObserverCallbacks = createCallbacks('requestMediaQueryObserver')
function onRequestMediaQueryObserver ({
reqId,
reqEnd,
res
}) {
const callback = requestMediaQueryObserverCallbacks.get(reqId)
if (callback) {
if (reqEnd) {
requestMediaQueryObserverCallbacks.pop(reqId)
return
}
callback(res)
}
}
if (__PLATFORM__ === 'h5') {
subscribe('onPageReady', createPageEvent('onReady'))
}
......@@ -59,4 +76,5 @@ export default function initSubscribe (subscribe, {
subscribe('onRequestComponentInfo', onRequestComponentInfo)
subscribe('onRequestComponentObserver', onRequestComponentObserver)
subscribe('onRequestMediaQueryObserver', onRequestMediaQueryObserver)
}
......@@ -73,7 +73,7 @@ export default {
initPolyfill(Vue)
lifecycleMixin(Vue)
/* eslint-disable no-undef */
if (typeof __UNI_ROUTER_BASE__ !== 'undefined') {
__uniConfig.router.base = __UNI_ROUTER_BASE__
......@@ -194,7 +194,11 @@ export default {
return uni.createIntersectionObserver(this, args)
}
Vue.prototype.createMediaQueryObserver = function createMediaQueryObserver (args) {
return uni.createMediaQueryObserver(this, args)
}
Vue.use(VueRouter)
}
}
}
......@@ -70,6 +70,10 @@ export function initPolyfill (Vue) {
return uni.createIntersectionObserver(this, options)
}
Vue.prototype.createMediaQueryObserver = function createMediaQueryObserver (options) {
return uni.createMediaQueryObserver(this, options)
}
Vue.prototype.selectComponent = function selectComponent (selector) {
return querySelector(this, parseSelector(selector))
}
......@@ -77,4 +81,4 @@ export function initPolyfill (Vue) {
Vue.prototype.selectAllComponents = function selectAllComponents (selector) {
return querySelectorAll(this, parseSelector(selector), [])
}
}
}
......@@ -11,9 +11,16 @@ import {
destroyComponentObserver
} from './request-component-observer'
import {
requestMediaQueryObserver,
destroyMediaQueryObserver
} from './request-media-query-observer'
export default {
setPageMeta,
requestComponentInfo,
requestComponentObserver,
destroyComponentObserver
}
destroyComponentObserver,
requestMediaQueryObserver,
destroyMediaQueryObserver
}
const mediaQueryObservers = {}
const listeners = {} // 用公用对象存储监听器
// 拼接媒体查询条件
function handleMediaQueryStr ($props) {
let mediaQueryStr = []
const propsMenu = [
'width',
'minWidth',
'maxWidth',
'height',
'minHeight',
'maxHeight',
'orientation'
]
for (const item of propsMenu) {
if (item !== 'orientation' && $props[item] !== '' && Number($props[item]) >= 0) {
mediaQueryStr.push(`(${humpToLine(item)}: ${Number($props[item])}px)`)
}
if (item === 'orientation' && $props[item]) {
mediaQueryStr.push(`(${humpToLine(item)}: ${$props[item]})`)
}
}
mediaQueryStr = mediaQueryStr.join(' and ')
return mediaQueryStr
}
function humpToLine (name) {
return name.replace(/([A-Z])/g, '-$1').toLowerCase()
}
// 请求媒体查询对象
export function requestMediaQueryObserver ({
reqId,
options
}, pageId) {
const pages = getCurrentPages()
const page = pages.find(page => page.$page.id === pageId)
if (!page) {
throw new Error(`Not Found:Page[${pageId}]`)
}
const pageVm = page.$vm
// 创建一个媒体查询对象
const mediaQueryObserver = mediaQueryObservers[reqId] = window.matchMedia(handleMediaQueryStr(options))
// 创建一个监听器
const listener = listeners[reqId] = e => {
UniViewJSBridge.publishHandler('onRequestMediaQueryObserver', {
reqId,
res: e.matches
}, pageVm.$page.id)
}
listener(mediaQueryObserver) // 监听前执行一次媒体查询
mediaQueryObserver.addListener(listener)
}
// 销毁媒体查询对象
export function destroyMediaQueryObserver ({
reqId
}) {
const listener = listeners[reqId] // 需要移除的某个监听
const mediaQueryObserver = mediaQueryObservers[reqId]
if (mediaQueryObserver) {
mediaQueryObserver.removeListener(listener) // 移除监听
delete mediaQueryObservers[reqId]
UniViewJSBridge.publishHandler('onRequestMediaQueryObserver', {
reqId,
reqEnd: true
})
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册