Skip to content

Commit 67feae2

Browse files
authored
Merge pull request #2427 from dos1in/master
feat(rn): 添加 pageScrollTo 方法支持
2 parents 1209a04 + 98dc6b6 commit 67feae2

7 files changed

Lines changed: 445 additions & 26 deletions

File tree

docs-vitepress/api-proxy/interface/scroll/pageScrollTo.md

Lines changed: 135 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,153 @@
22

33
将页面滚动到目标位置,支持选择器和滚动距离两种方式定位
44

5-
支持情况: 微信、支付宝、web
5+
支持情况: 微信、支付宝、RN、web
66

77
[参考文档](https://developers.weixin.qq.com/miniprogram/dev/api/ui/scroll/wx.pageScrollTo.html)
88

99
### 参数 {#parameters}
1010
**Object object**
1111

12-
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 | 支付宝 | web |
13-
|-------------|----------|--------|------|----------------------------------------------------------------------|----------|--------|-----|
14-
| scrollTop | number | || 滚动到页面的目标位置,单位 px | | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> |
15-
| duration | number | 300 || 滚动动画的时长,单位 ms | | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> |
16-
| selector | string | || 选择器 | 2.7.3 | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> |
17-
| offsetTop | number | || 偏移距离,需要和 selector 参数搭配使用,可以滚动到 selector 加偏移距离的位置,单位 px | 2.23.1 | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: red; font-weight: bold;'>✗</span> |
18-
| success | function | || 接口调用成功的回调函数 | | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> |
19-
| fail | function | || 接口调用失败的回调函数 | | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> |
20-
| complete | function | || 接口调用结束的回调函数(调用成功、失败都会执行) | | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> |
21-
22-
12+
| 属性 | 类型 | 默认值 | 必填 | 说明 | 最低版本 | 支付宝 | web | RN |
13+
|-------------|----------|--------|------|----------------------------------------------------------------------|----------|--------|-----|-----|
14+
| scrollTop | number | || 滚动到页面的目标位置,单位 px | | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> |
15+
| duration | number | 300 || 滚动动画的时长,单位 ms | | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> |
16+
| selector | string | || 选择器 | 2.7.3 | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> |
17+
| offsetTop | number | || 偏移距离,需要和 selector 参数搭配使用,可以滚动到 selector 加偏移距离的位置,单位 px | 2.23.1 | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: red; font-weight: bold;'>✗</span> | <span style='color: green; font-weight: bold;'>✓</span> |
18+
| success | function | || 接口调用成功的回调函数 | | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> |
19+
| fail | function | || 接口调用失败的回调函数 | | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> |
20+
| complete | function | || 接口调用结束的回调函数(调用成功、失败都会执行) | | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> | <span style='color: green; font-weight: bold;'>✓</span> |
2321

2422
### 示例代码 {#example-code}
2523

24+
#### 使用 scrollTop 滚动到指定位置
25+
2626
```js
2727
mpx.pageScrollTo({
2828
scrollTop: 0,
2929
duration: 300
3030
})
3131
```
32+
33+
#### 使用 selector 滚动到指定元素(小程序)
34+
35+
```xml
36+
<view id="target-section">目标区域</view>
37+
```
38+
39+
```js
40+
mpx.pageScrollTo({
41+
selector: '#target-section',
42+
duration: 300
43+
})
44+
```
45+
46+
#### 使用 scrollTop 滚动到顶部(React Native)
47+
48+
::: warning React Native 平台特殊说明
49+
在 React Native 平台上使用 `pageScrollTo` 时,需要在页面的 scroll-view 组件上声明固定的 `wx:ref="pageScrollView"`,框架将通过该固定 ref 定位滚动容器。
50+
:::
51+
52+
```xml
53+
<template>
54+
<view class="page">
55+
<!-- scroll-view 必须声明固定 ref 名称 pageScrollView -->
56+
<scroll-view wx:ref="pageScrollView" scroll-y="{{true}}" style="height: 100vh;">
57+
<view style="height: 500px;">顶部内容</view>
58+
<view style="height: 500px;">底部内容</view>
59+
</scroll-view>
60+
</view>
61+
</template>
62+
63+
<script>
64+
import { createPage } from '@mpxjs/core'
65+
66+
createPage({
67+
methods: {
68+
scrollToTop() {
69+
mpx.pageScrollTo({
70+
scrollTop: 0,
71+
duration: 300
72+
})
73+
}
74+
}
75+
})
76+
</script>
77+
```
78+
79+
#### 使用 selector 滚动到指定元素(React Native)
80+
81+
```xml
82+
<template>
83+
<view class="page">
84+
<!-- scroll-view 必须声明固定 ref 名称 pageScrollView -->
85+
<scroll-view wx:ref="pageScrollView" scroll-y="{{true}}" style="height: 100vh;">
86+
<view style="height: 500px;">顶部内容</view>
87+
88+
<!-- 目标元素需要同时添加 id 和 wx:ref -->
89+
<view id="target-section" wx:ref style="height: 200px;">
90+
目标区域
91+
</view>
92+
93+
<view style="height: 500px;">底部内容</view>
94+
</scroll-view>
95+
</view>
96+
</template>
97+
98+
<script>
99+
import { createPage } from '@mpxjs/core'
100+
101+
createPage({
102+
methods: {
103+
scrollToTarget() {
104+
mpx.pageScrollTo({
105+
selector: '#target-section',
106+
duration: 300,
107+
offsetTop: 10, // 可选:偏移 10px
108+
success: () => {
109+
console.log('滚动成功')
110+
},
111+
fail: (err) => {
112+
console.error('滚动失败:', err)
113+
}
114+
})
115+
}
116+
}
117+
})
118+
</script>
119+
```
120+
121+
### React Native 平台注意事项
122+
123+
#### 必需的配置
124+
125+
1. **scroll-view 必须声明固定 ref 名称 `pageScrollView`**
126+
```xml
127+
<!-- ✅ 正确 -->
128+
<scroll-view wx:ref="pageScrollView" scroll-y="{{true}}">
129+
<!-- 内容 -->
130+
</scroll-view>
131+
132+
<!-- ❌ 错误:缺少 wx:ref="pageScrollView" -->
133+
<scroll-view scroll-y="{{true}}">
134+
<!-- 内容 -->
135+
</scroll-view>
136+
```
137+
138+
2. **使用 selector 时,目标元素必须同时有 id 和 wx:ref**
139+
```xml
140+
<!-- ✅ 正确:同时有 id 和 wx:ref -->
141+
<view id="section-1" wx:ref>区域 1</view>
142+
143+
<!-- ❌ 错误:只有 id,没有 wx:ref -->
144+
<view id="section-1">区域 1</view>
145+
```
146+
147+
3. **启用滚动方向**
148+
```xml
149+
<!-- ✅ 正确:启用纵向滚动 -->
150+
<scroll-view scroll-y="{{true}}">
151+
152+
<!-- ❌ 错误:没有启用滚动 -->
153+
<scroll-view>
154+
```

packages/api-proxy/src/platform/api/base/rnCanIUseConfig.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,16 @@ export const SUPPORTED_APIS = [
9393
'clearStorage',
9494
'clearStorageSync',
9595

96+
// page-scroll-to
97+
'pageScrollTo',
98+
9699
// system
97100
'getSystemInfo',
98101
'getSystemInfoSync',
102+
'getDeviceInfo',
103+
'getWindowInfo',
104+
'getLaunchOptionsSync',
105+
'getEnterOptionsSync',
99106

100107
// setting
101108
'getMenuButtonBoundingClientRect',
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './rnPageScrollTo'
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { successHandle, failHandle, getFocusedNavigation } from '../../../common/js'
2+
3+
/**
4+
* 实现 React Native 下的 pageScrollTo
5+
* 支持 scrollTop 和 selector 两种定位方式
6+
*/
7+
export function pageScrollTo (options = {}) {
8+
const {
9+
scrollTop,
10+
duration = 300,
11+
selector,
12+
offsetTop = 0,
13+
success,
14+
fail,
15+
complete
16+
} = options
17+
18+
const navigation = getFocusedNavigation()
19+
20+
if (!navigation?.pageScrollTo) {
21+
return failHandle({
22+
errMsg: 'pageScrollTo:fail page instance not found'
23+
}, fail, complete)
24+
}
25+
26+
// 验证参数
27+
if (scrollTop === undefined && !selector) {
28+
return failHandle({
29+
errMsg: 'pageScrollTo:fail scrollTop or selector is required'
30+
}, fail, complete)
31+
}
32+
33+
try {
34+
navigation.pageScrollTo({
35+
scrollTop,
36+
duration,
37+
selector,
38+
offsetTop,
39+
onSuccess: () => {
40+
successHandle({
41+
errMsg: 'pageScrollTo:ok'
42+
}, success, complete)
43+
},
44+
onFail: (errMsg) => {
45+
failHandle({
46+
errMsg: errMsg || 'pageScrollTo:fail'
47+
}, fail, complete)
48+
}
49+
})
50+
} catch (e) {
51+
failHandle({
52+
errMsg: `pageScrollTo:fail ${e.message}`
53+
}, fail, complete)
54+
}
55+
}

packages/core/src/platform/builtInMixins/index.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ export default function getBuiltInMixins ({ type, rawOptions = {} }) {
2626
refsMixin(),
2727
i18nMixin(),
2828
relationsMixin(type),
29-
pageRouteMixin(type)
29+
pageRouteMixin(type),
30+
pageScrollMixin(type)
3031
]
3132
} else if (isWeb) {
3233
bulitInMixins = [
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
import { warn } from '@mpxjs/utils'
2+
import { CREATED } from '../../core/innerLifecycle'
3+
4+
/**
5+
* React Native 页面滚动 Mixin
6+
* 提供页面级别的 pageScrollTo 方法
7+
* 使用该功能需在页面的 scroll-view 组件上声明 wx:ref="pageScrollView"
8+
*/
9+
export default function pageScrollMixin (mixinType) {
10+
if (mixinType !== 'page') {
11+
return
12+
}
13+
14+
return {
15+
[CREATED] () {
16+
this.__registerPageScrollTo()
17+
},
18+
beforeUnmount () {
19+
const navigation = this.__props?.navigation
20+
if (navigation && navigation.pageScrollTo) {
21+
delete navigation.pageScrollTo
22+
}
23+
},
24+
methods: {
25+
/**
26+
* 注册 pageScrollTo 方法到 navigation 对象
27+
*/
28+
__registerPageScrollTo () {
29+
const navigation = this.__props?.navigation
30+
31+
// navigation.pageScrollTo 不存在时才注册,避免重复
32+
if (navigation && !navigation.pageScrollTo) {
33+
navigation.pageScrollTo = (options) => {
34+
this.__pageScrollTo(options)
35+
}
36+
}
37+
},
38+
39+
/**
40+
* 获取页面滚动视图节点(通过固定 ref 名称 pageScrollView)
41+
* @returns {Object|null} 滚动视图的节点实例
42+
*/
43+
__findScrollableNode () {
44+
const ref = this.__refs?.pageScrollView?.[0]
45+
if (!ref || ref.type !== 'node' || !ref.instance?.getNodeInstance) return null
46+
return ref.instance.getNodeInstance()
47+
},
48+
49+
/**
50+
* 页面滚动到指定位置
51+
* @param {Object} options - 配置选项
52+
* @param {number} options.scrollTop - 滚动到页面的目标位置(单位 px)
53+
* @param {number} options.duration - 滚动动画的时长(单位 ms)
54+
* @param {string} options.selector - 选择器
55+
* @param {number} options.offsetTop - 偏移距离
56+
* @param {Function} options.onSuccess - 成功回调
57+
* @param {Function} options.onFail - 失败回调
58+
*/
59+
__pageScrollTo (options = {}) {
60+
const {
61+
scrollTop,
62+
duration = 300,
63+
selector,
64+
offsetTop = 0,
65+
onSuccess,
66+
onFail
67+
} = options
68+
69+
try {
70+
const nodeInstance = this.__findScrollableNode()
71+
72+
if (nodeInstance) {
73+
this.__executeScroll(nodeInstance, scrollTop, duration, selector, offsetTop, onSuccess, onFail)
74+
return
75+
}
76+
77+
// 没找到可滚动视图
78+
const errMsg = 'pageScrollTo:fail scrollable view not found. Please add wx:ref="pageScrollView" to the scroll-view component in your page'
79+
warn(errMsg)
80+
onFail && onFail(errMsg)
81+
} catch (e) {
82+
const errMsg = `pageScrollTo:fail ${e.message}`
83+
warn(errMsg)
84+
onFail && onFail(errMsg)
85+
}
86+
},
87+
88+
/**
89+
* 执行滚动操作
90+
*/
91+
__executeScroll (scrollViewNodeInstance, scrollTop, duration, selector, offsetTop, onSuccess, onFail) {
92+
try {
93+
const scrollViewNode = scrollViewNodeInstance.instance.node
94+
95+
// 如果提供了 selector,使用 scrollIntoView
96+
if (selector) {
97+
if (scrollViewNode.scrollIntoView) {
98+
scrollViewNode.scrollIntoView(selector, {
99+
offset: offsetTop,
100+
animated: duration > 0,
101+
duration
102+
})
103+
onSuccess && onSuccess()
104+
} else {
105+
const errMsg = 'pageScrollTo:fail scrollIntoView method not available'
106+
warn(errMsg)
107+
onFail && onFail(errMsg)
108+
}
109+
return
110+
}
111+
112+
// 使用 scrollTop 进行滚动
113+
if (scrollTop !== undefined) {
114+
if (scrollViewNode.scrollTo) {
115+
scrollViewNode.scrollTo({
116+
top: scrollTop,
117+
left: 0,
118+
animated: duration > 0,
119+
duration
120+
})
121+
onSuccess && onSuccess()
122+
} else {
123+
const errMsg = 'pageScrollTo:fail scrollTo method not available'
124+
warn(errMsg)
125+
onFail && onFail(errMsg)
126+
}
127+
return
128+
}
129+
130+
// 既没有 scrollTop 也没有 selector
131+
const errMsg = 'pageScrollTo:fail scrollTop or selector is required'
132+
warn(errMsg)
133+
onFail && onFail(errMsg)
134+
} catch (e) {
135+
const errMsg = `pageScrollTo:fail ${e.message}`
136+
warn(errMsg)
137+
onFail && onFail(errMsg)
138+
}
139+
}
140+
}
141+
}
142+
}

0 commit comments

Comments
 (0)