Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .agents/skills/mpx2rn/references/rn-api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -1849,6 +1849,10 @@ mpx.config.rnConfig.wifiPermission = () => {
| `nodesRef.context(callback)` / `node(callback)` / `ref(callback)` | 当前 `SelectorQuery` | 分别返回 `{ context }`、`{ node }`、`{ ref }`。 |
| `query.exec(callback)` | `undefined` | 按入队顺序聚合前述回调结果,最终参数为结果数组。 |

通过 `query.select(selector).scrollOffset(callback)` 查询实际的 `scroll-view` 节点时,回调结果包含当前滚动位置以及滚动内容的实际 `scrollWidth`、`scrollHeight`。

RN 当前支持动态读写 `ScrollViewContext` 的 `scrollEnabled`、`bounces`、`showScrollbar` 和 `pagingEnabled` 四个属性。使用时需要为 `scroll-view` 开启 `enhanced`,并通过 `query.select(selector).node(callback)` 回调参数中的 `node` 字段获取。

#### 注意事项

- selector 仅支持 `#id`、`.class` 和连续 class(如 `.a.b`),不支持后代、子节点、跨组件等复杂选择器。对应模板节点须添加空 `wx:ref`,并在节点渲染完成后查询;数据更新后需等待 `this.$nextTick()` 再查询。
Expand Down
4 changes: 4 additions & 0 deletions docs-vitepress/guide/rn/application-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ module.exports = {

使用 `createSelectorQuery` 来获取基础组件需要在基础节点上标记 `wx:ref` 标签才能生效,以及所支持的选择器范围和 `selectComponent`/`selectAllComponents` 一致。

查询实际的 `scroll-view` 节点时,可通过 `select(selector).scrollOffset(callback)` 获取包含实际内容宽高的回调结果。

RN 当前支持动态读写 `ScrollViewContext` 的 `scrollEnabled`、`bounces`、`showScrollbar` 和 `pagingEnabled` 四个属性。使用时需要为 `scroll-view` 开启 `enhanced`,并通过 `select(selector).node(callback)` 回调参数中的 `node` 字段获取。

```javascript
<template>
<view wx:ref class="title">this is view</view>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import Animated, { useSharedValue, withTiming, useAnimatedStyle, runOnJS } from
import { warn, hasOwn } from '@mpxjs/utils'
import useInnerProps, { getCustomEvent } from './getInnerListeners'
import useNodesRef, { HandlerRef } from './useNodesRef'
import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, extendObject, flatGesture, GestureHandler, hiddenStyle, useRunOnJSCallback, useTextPassThrough } from './utils'
import { splitProps, splitStyle, useTransformStyle, useLayout, wrapChildren, extendObject, flatGesture, GestureHandler, hiddenStyle, useRunOnJSCallback, useTextPassThrough, useUpdateEffect } from './utils'
import { IntersectionObserverContext, ScrollViewContext } from './context'
import Portal from './mpx-portal'
import * as perf from '@mpxjs/perf'
Expand Down Expand Up @@ -109,6 +109,16 @@ type ScrollAdditionalProps = {
onScrollEndDrag?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void
onMomentumScrollEnd?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void
}
type ScrollViewContextType = {
scrollEnabled: boolean
bounces: boolean
showScrollbar: boolean
pagingEnabled: boolean
fastDeceleration: boolean
decelerationDisabled: boolean
scrollTo: (options: { top?: number; left?: number; animated?: boolean; duration?: number }) => void
scrollIntoView: (selector: string, options: { offset?: number; animated?: boolean }) => void
}

const AnimatedScrollView = RNAnimated.createAnimatedComponent(ScrollView) as React.ComponentType<any>

Expand Down Expand Up @@ -176,7 +186,31 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
if (__mpx_perf_framework__) idStyle = perf.scopeStart('scroll-view:render:style')
const [refreshing, setRefreshing] = useState(false)
const [enableScroll, setEnableScroll] = useState(true)
// 下拉刷新内部维护的 bounces 状态
const [scrollBounces, setScrollBounces] = useState(false)
// 外部用户传递的 bounces 属性
const [bouncesState, setBouncesState] = useState(!!bounces)
const [showScrollbarState, setShowScrollbarState] = useState(!!showScrollbar)
const [pagingEnabledState, setPagingEnabledState] = useState(!!pagingEnabled)

// 用于 ScrollViewContext getter 访问最新状态
const scrollViewStateRef = useRef({
enableScroll: true,
bounces: false,
showScrollbar,
pagingEnabled,
scrollX,
scrollY
})

scrollViewStateRef.current = {
enableScroll,
bounces: bouncesState,
showScrollbar: showScrollbarState,
pagingEnabled: pagingEnabledState,
scrollX,
scrollY
}

const enableScrollValue = useSharedValue(true)
const bouncesValue = useSharedValue(false)
Expand All @@ -189,6 +223,8 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
offset: 0,
scrollLeft: 0,
scrollTop: 0,
scrollWidth: 0,
scrollHeight: 0,
visibleLength: 0
})

Expand Down Expand Up @@ -236,22 +272,64 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
})
const runOnJSCallback = useRunOnJSCallback(runOnJSCallbackRef)

// 创建 ScrollViewContext 对象,支持动态读写属性
const scrollViewContext = useRef<ScrollViewContextType | null>(null)

if (!scrollViewContext.current) {
scrollViewContext.current = {
get scrollEnabled () {
const { enableScroll, scrollX, scrollY } = scrollViewStateRef.current
return !enableScroll ? false : !!(scrollX || scrollY)
},
set scrollEnabled (value: boolean) {
setEnableScroll(value)
},
get bounces () {
return scrollViewStateRef.current.bounces
},
set bounces (value: boolean) {
setBouncesState(value)
},
get showScrollbar () {
return scrollViewStateRef.current.showScrollbar
},
set showScrollbar (value: boolean) {
setShowScrollbarState(value)
},
get pagingEnabled () {
return scrollViewStateRef.current.pagingEnabled
},
set pagingEnabled (value: boolean) {
setPagingEnabledState(value)
},
get fastDeceleration () {
return false
},
set fastDeceleration (value: boolean) {
warn(`fastDeceleration is not supported in ${__mpx_mode__} platform`)
},
get decelerationDisabled () {
return false
},
set decelerationDisabled (value: boolean) {
warn(`decelerationDisabled is not supported in ${__mpx_mode__} platform`)
},
scrollTo,
scrollIntoView: handleScrollIntoView
}
}

useNodesRef(props, ref, scrollViewRef, {
style: normalStyle,
scrollOffset: scrollOptions,
node: {
scrollEnabled: scrollX || scrollY,
bounces,
showScrollbar,
pagingEnabled,
fastDeceleration: false,
decelerationDisabled: false,
scrollTo,
scrollIntoView: handleScrollIntoView
},
node: scrollViewContext.current,
gestureRef: scrollViewRef
})

useUpdateEffect(() => { setBouncesState(!!bounces) }, [bounces])
useUpdateEffect(() => { setShowScrollbarState(!!showScrollbar) }, [showScrollbar])
useUpdateEffect(() => { setPagingEnabledState(!!pagingEnabled) }, [pagingEnabled])

const { layoutRef, layoutStyle, layoutProps } = useLayout({ props, hasSelfPercent, setWidth, setHeight, nodeRef: scrollViewRef, onLayout })

const contextValue = useMemo(() => {
Expand Down Expand Up @@ -397,6 +475,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
nodeRef.current.measureLayout(
targetScrollView,
(left: number, top: number) => {
const { scrollX, scrollY } = scrollViewStateRef.current
const adjustedLeft = scrollX ? left + offset : left
const adjustedTop = scrollY ? top + offset : top

Expand Down Expand Up @@ -474,6 +553,8 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
const newContentLength = selectLength({ height, width })
const oldContentLength = scrollOptions.current.contentLength
scrollOptions.current.contentLength = newContentLength
scrollOptions.current.scrollWidth = width
scrollOptions.current.scrollHeight = height
// 内容高度变化时,Animated.event 的映射可能会有不生效的场景,所以需要手动设置一下 scrollOffset 的值
if (enableSticky && (__mpx_mode__ === 'android' || __mpx_mode__ === 'ios')) {
// 当内容变少时,检查当前滚动位置是否超出新的内容范围
Expand Down Expand Up @@ -756,8 +837,8 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
horizontal: scrollX && !scrollY,
scrollEventThrottle: scrollEventThrottle,
scrollsToTop: enableBackToTop,
showsHorizontalScrollIndicator: scrollX && showScrollbar,
showsVerticalScrollIndicator: scrollY && showScrollbar,
showsHorizontalScrollIndicator: scrollX && showScrollbarState,
showsVerticalScrollIndicator: scrollY && showScrollbarState,
scrollEnabled: !enableScroll ? false : !!(scrollX || scrollY),
bounces: false,
overScrollMode: 'never',
Expand All @@ -776,8 +857,8 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S

if (enhanced) {
Object.assign(scrollAdditionalProps, {
bounces: hasRefresher ? scrollBounces : !!bounces,
pagingEnabled
bounces: hasRefresher ? bouncesState && scrollBounces : bouncesState,
pagingEnabled: pagingEnabledState
})
}

Expand Down Expand Up @@ -835,7 +916,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
.failOffsetX([-5, 5])
.onUpdate((event) => {
'worklet'
if (enhanced && !!bounces) {
if (enhanced && bouncesState) {
if (event.translationY > 0 && bouncesValue.value) {
updateBouncesState(false)
} else if ((event.translationY < 0) && !bouncesValue.value) {
Expand Down Expand Up @@ -892,7 +973,7 @@ const _ScrollView = forwardRef<HandlerRef<ScrollView & View, ScrollViewProps>, S
}
})
.simultaneousWithExternalGesture(scrollViewRef)
}, [enhanced, bounces, refreshing, refresherThreshold])
}, [enhanced, bouncesState, refreshing, refresherThreshold])

const ScrollViewComponent = enableSticky ? AnimatedScrollView : ScrollView

Expand Down
Loading