You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native Reanimated 3:点击按钮停止ScrollView滚动问题求助

问题分析

你当前代码的核心问题有两个:

  1. useAnimatedReaction的硬编码滚动:每次scrollY变化时都会强制调用scrollTo(scrollRef, 0, 1000, true),这会直接触发滚动到底部的动画,完全覆盖了其他滚动操作。
  2. 错误的停止逻辑:cancelAnimation(scrollY)只能终止scrollY自身的动画,但无法中断scrollTo已经发起的滚动动画,也没有控制自动滚动的开关状态。
解决方案

我们需要新增一个状态变量控制自动滚动的启停,同时用Reanimated的动画API实现带条件的循环滚动,确保能随时终止动画。以下是修正后的代码:

import React from 'react';
import { Button, StyleSheet, View } from 'react-native';
import Animated, {
  scrollTo,
  useSharedValue,
  useAnimatedScrollHandler,
  useAnimatedReaction,
  useAnimatedRef,
  withRepeat,
  withSequence,
  withTiming,
  cancelAnimation,
  runOnJS,
} from 'react-native-reanimated';

const AnimatedScrollView = () => {
  const scrollY = useSharedValue(0);
  const scrollRef = useAnimatedRef();
  // 控制自动滚动的开关
  const isAutoScrolling = useSharedValue(false);
  // 自动滚动方向:1向下,-1向上
  const scrollDirection = useSharedValue(1);

  const scrollHandler = useAnimatedScrollHandler({
    onScroll: ({ contentOffset: { y } }) => {
      scrollY.value = y;
    },
  });

  const startScroll = (direction = 1) => {
    isAutoScrolling.value = true;
    scrollDirection.value = direction;
    
    // 启动循环滚动动画
    scrollY.value = withRepeat(
      withSequence(
        withTiming(scrollY.value + direction * 500, { duration: 1000 }),
        () => {
          // 滚动后更新ScrollView位置
          scrollTo(scrollRef, 0, scrollY.value, false);
          // 检查是否到达边界(这里简化处理,实际需根据内容高度计算)
          const contentHeight = 1800; // 6个300px容器的总高度
          const screenHeight = 600; // 假设屏幕高度,实际需用Dimensions获取
          const isAtBottom = scrollY.value >= contentHeight - screenHeight;
          const isAtTop = scrollY.value <= 0;
          if (isAtBottom || isAtTop) {
            runOnJS(stopScroll)();
          }
        }
      ),
      -1, // 无限重复
      false
    );
  };

  const stopScroll = () => {
    isAutoScrolling.value = false;
    cancelAnimation(scrollY);
  };

  // 监听自动滚动状态,确保状态变化时终止动画
  useAnimatedReaction(
    () => isAutoScrolling.value,
    (isScrolling) => {
      if (!isScrolling) {
        cancelAnimation(scrollY);
      }
    }
  );

  return (
    <>
      <Button title="向下滚动" onPress={() => startScroll(1)} />
      <Button title="向上滚动" onPress={() => startScroll(-1)} />
      <Button title="停止滚动" onPress={stopScroll} />
      <Animated.ScrollView
        onScroll={scrollHandler}
        scrollEventThrottle={16}
        ref={scrollRef}
        scrollEnabled={!isAutoScrolling.value} // 自动滚动时禁用手动滚动(可选)
      >
        {[...Array(6)].map((_, index) => (
          <View
            key={index}
            style={[
              styles.container,
              { backgroundColor: ['pink', 'blue', 'red'][index % 3] },
            ]}
          />
        ))}
      </Animated.ScrollView>
    </>
  );
};

export default AnimatedScrollView;

const styles = StyleSheet.create({
  container: {
    height: 300,
    width: '100%',
  },
});
关键改进点
  • isAutoScrolling开关:用这个状态变量控制自动滚动的启停,stopScroll时将其设为false,并通过useAnimatedReaction监听状态变化终止动画。
  • 条件循环滚动:使用withRepeat实现无限滚动,但在每次滚动后检查是否到达边界,到达则自动停止。
  • 方向控制:新增scrollDirection变量支持上下滚动,适配你拖拽到顶部/底部触发不同方向滚动的需求。
  • 修复硬编码滚动:移除原代码中强制滚动到底部的逻辑,改为基于scrollY的动态偏移。
适配拖拽触发逻辑

针对你“拖拽容器到顶部/底部触发自动滚动”的需求,只需在拖拽结束时判断容器位置:

  • 若容器拖拽至页面顶部,调用startScroll(-1)触发向上滚动
  • 若拖拽至页面底部,调用startScroll(1)触发向下滚动
  • 拖拽停止或容器离开边界时,调用stopScroll()终止动画

内容的提问来源于stack exchange,提问作者Vic Rokx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 04:05:42