ScrollView

版本要求:
@ray-js/ray≥0.5.10

描述

可滚动视图容器,支持横向或纵向滚动,可配置下拉刷新、滚动事件监听等功能。

属性

属性类型必填默认值说明最低版本
scrollXboolean否false允许横向滚动-
scrollYboolean否false允许纵向滚动-
upperThresholdnumber | string否50距顶部/左边多远时,触发 scrolltoupper 事件-
lowerThresholdnumber | string否50距底部/右边多远时,触发 scrolltolower 事件-
scrollTopnumber | string否0设置竖向滚动条位置-
scrollLeftnumber | string否0设置横向滚动条位置-
scrollIntoViewstring否-值应为某子元素 id(id 不能以数字开头)。设置哪个方向可滚动,则在哪个方向滚动到该元素-
scrollIntoViewOffsetnumber否0跳转到 scrollIntoView 目标节点时的额外偏移,单位 pxv1.7.58
scrollWithAnimationboolean否false在设置滚动条位置时使用动画过渡-
否-滚动时触发-
否-滚动到顶部/左边时触发-
否-滚动到底部/右边时触发-
refresherEnabledboolean否false开启自定义下拉刷新v0.9.3
refresherThresholdnumber否45设置自定义下拉刷新阈值v0.9.3
refresherDefaultStyle"black" | "white" | "none" | "auto"否black设置自定义下拉刷新默认样式v0.9.3
refresherBackgroundstring否#FFF设置自定义下拉刷新区域背景颜色v0.9.3
refresherTriggeredboolean否false设置当前下拉刷新状态,true 表示下拉刷新已经被触发,false 表示下拉刷新未被触发v0.9.3
hideScrollbarboolean否true隐藏滚动条-
bouncesboolean否true是否启用iOS滚动回弹效果(iOS 16.0+ 完全支持)v1.7.58
否-自定义下拉刷新控件被下拉时触发v0.9.3
否-自定义下拉刷新被触发时触发v0.9.3
否-自定义下拉刷新被复位时触发v0.9.3
否-自定义下拉刷新被中止时触发v0.9.3

引用对象

interfaceRefresherPullingEvent
export interface RefresherPullingEvent extends BaseEvent {
  /** 事件类型 */
  type: 'refresherpulling';
}
interfaceRefresherRefreshEvent
export interface RefresherRefreshEvent extends BaseEvent {
  /** 事件类型 */
  type: 'refresherrefresh';
}
interfaceRefresherRestoreEvent
export interface RefresherRestoreEvent extends BaseEvent {
  /** 事件类型 */
  type: 'refresherrestore';
}
interfaceRefresherAbortEvent
export interface RefresherAbortEvent extends BaseEvent {
  /** 事件类型 */
  type: 'refresherabort';
}
interfaceBaseEvent
属性类型必填描述
typestring是事件类型
timeStampnumber是页面打开到触发事件所经过的毫秒数
是触发事件的源组件
是当前组件的一些属性值集合
markany否事件标记数据
interfaceTarget
属性类型必填描述
idstring是事件源组件的id
是事件源组件上的 `dataset` 自定义属性组成的集合

合法值

ScrollDirectionDetail.direction
值说明最低版本
top滚动到顶部-
left滚动到左边-
bottom滚动到底部-
right滚动到右边-
refresherDefaultStyle
值说明最低版本
black黑色刷新指示器-
white白色刷新指示器-
none不使用默认刷新指示器,可自行实现刷新样式-
auto刷新指示器颜色跟随 App 主题-

示例代码

基础用法

import React from 'react';
import { ScrollView, View, Text } from '@ray-js/ray';

export default function BasicScrollView() {
  const handleScrollToUpper = () => {
    console.log('已滚动到顶部');
  };

  const handleScrollToLower = () => {
    console.log('已滚动到底部');
  };

  return (
    <ScrollView
      scrollY
      style={{ height: '400rpx', backgroundColor: '#f5f5f5' }}
      onScrollToUpper={handleScrollToUpper}
      onScrollToLower={handleScrollToLower}
    >
      {Array.from({ length: 10 }, (_, i) => (
        <View
          key={i}
          style={{
            height: '100rpx',
            margin: '10rpx',
            backgroundColor: '#fff',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
        >
          <Text>列表项 {i + 1}</Text>
        </View>
      ))}
    </ScrollView>
  );
}

下拉刷新

import React, { useState, useCallback } from 'react';
import { ScrollView, View, Text } from '@ray-js/ray';

export default function PullToRefreshDemo() {
  const [list, setList] = useState(() =>
    Array.from({ length: 10 }, (_, i) => `初始数据 ${i + 1}`)
  );
  const [refreshing, setRefreshing] = useState(false);
  const [loading, setLoading] = useState(false);

  const loadData = useCallback((startIndex: number) => {
    return new Promise<string[]>((resolve) => {
      setTimeout(() => {
        const newList = Array.from(
          { length: 10 },
          (_, i) => `数据 ${startIndex + i + 1}`
        );
        resolve(newList);
      }, 1000);
    });
  }, []);

  const handleRefresh = useCallback(async () => {
    setRefreshing(true);
    try {
      const newList = await loadData(-1);
      setList(prevList => [...newList, ...prevList]);
    } finally {
      setRefreshing(false);
    }
  }, [loadData]);

  const handleScrollToLower = useCallback(async () => {
    if (loading) return;
    setLoading(true);
    try {
      const newList = await loadData(list.length);
      setList(prevList => [...prevList, ...newList]);
    } finally {
      setLoading(false);
    }
  }, [loading, list.length, loadData]);

  return (
    <ScrollView
      scrollY
      style={{ height: '600rpx', backgroundColor: '#f5f5f5' }}
      refresherEnabled
      refresherThreshold={50}
      refresherTriggered={refreshing}
      refresherBackground="#f5f5f5"
      onRefresherrefresh={handleRefresh}
      onScrollToLower={handleScrollToLower}
      lowerThreshold={100}
    >
      {list.map((item, index) => (
        <View
          key={index}
          style={{
            height: '100rpx',
            margin: '10rpx 20rpx',
            backgroundColor: '#fff',
            borderRadius: '8rpx',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
        >
          <Text>{item}</Text>
        </View>
      ))}
      {loading && (
        <View
          style={{
            height: '80rpx',
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
        >
          <Text style={{ color: '#999', fontSize: '28rpx' }}>加载中...</Text>
        </View>
      )}
    </ScrollView>
  );
}

横向滚动

import React from 'react';
import { ScrollView, View, Text } from '@ray-js/ray';

export default function HorizontalScrollDemo() {
  const cards = ['卡片1', '卡片2', '卡片3', '卡片4', '卡片5'];

  return (
    <ScrollView
      scrollX
      style={{
        width: '100%',
        height: '200rpx',
        whiteSpace: 'nowrap',
      }}
      onScroll={(e) => console.log('横向滚动位置:', e.detail.scrollLeft)}
    >
      {cards.map((card, index) => (
        <View
          key={index}
          style={{
            width: '300rpx',
            height: '180rpx',
            marginRight: '20rpx',
            backgroundColor: '#1890ff',
            borderRadius: '12rpx',
            display: 'inline-flex',
            alignItems: 'center',
            justifyContent: 'center',
          }}
        >
          <Text style={{ color: '#fff', fontSize: '32rpx' }}>{card}</Text>
        </View>
      ))}
    </ScrollView>
  );
}

滚动到指定位置

import React, { useState } from 'react';
import { ScrollView, View, Text, Button } from '@ray-js/ray';

export default function ScrollIntoViewDemo() {
  const [targetId, setTargetId] = useState('');
  const sections = ['section-a', 'section-b', 'section-c', 'section-d'];

  return (
    <View>
      <View style={{ display: 'flex', marginBottom: '20rpx' }}>
        {sections.map((id) => (
          <Button
            key={id}
            size="mini"
            style={{ marginRight: '10rpx' }}
            onClick={() => setTargetId(id)}
          >
            跳转 {id.split('-')[1].toUpperCase()}
          </Button>
        ))}
      </View>
      <ScrollView
        scrollY
        scrollWithAnimation
        scrollIntoView={targetId}
        scrollIntoViewOffset={10}
        style={{ height: '400rpx', backgroundColor: '#f5f5f5' }}
      >
        {sections.map((id, index) => (
          <View
            key={id}
            id={id}
            style={{
              height: '300rpx',
              margin: '10rpx',
              backgroundColor: ['#e6f7ff', '#fff7e6', '#f6ffed', '#fff1f0'][index],
              display: 'flex',
              alignItems: 'center',
              justifyContent: 'center',
            }}
          >
            <Text style={{ fontSize: '36rpx' }}>区域 {id.split('-')[1].toUpperCase()}</Text>
          </View>
        ))}
      </ScrollView>
    </View>
  );
}

常见问题

为何 scroll-view 在 popup 扩展组件中无法滑动?

popup 组件上加上 disableScroll 属性并将值设为 false 才能滑动。

如何监听 scroll-view 滚动到底部?

可以直接在 onScroll 方法中进行处理,使用 onScrollToLower 监听 scrollView 的滚动高度来进行判断是否滑动到了底部。 scrollHeight 是 scrollView 里面所有 View 的高度和,scrollTop 是滚动的值;