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

如何在用户滚动时禁用react-native-reanimated 2的日历组件动画

解决ScrollView滚动触发日历切换动画的问题

核心思路是仅在周/月视图切换阶段启用动画,滚动时完全禁用,通过父组件控制动画开关状态,子组件根据状态决定是否加载动画配置。

步骤1:父组件维护动画开关状态

在父组件中新增状态跟踪动画是否启用,同时监听ScrollView滚动事件,滚动时立即禁用动画;切换视图时临时启用动画,待动画周期结束后自动关闭。

import { useState } from 'react';
import { ScrollView, Button } from 'react-native';

const CalendarContainer = () => {
  const [mode, setMode] = useState('week');
  // 控制动画是否启用的状态
  const [isAnimationEnabled, setIsAnimationEnabled] = useState(false);
  // 跟踪是否正在滚动
  const [isScrolling, setIsScrolling] = useState(false);

  // 周/月视图切换逻辑
  const toggleCalendarMode = () => {
    // 切换时启用动画
    setIsAnimationEnabled(true);
    setMode(prev => prev === 'week' ? 'month' : 'week');
    // 根据最长动画时长(这里取500ms),动画结束后禁用
    setTimeout(() => setIsAnimationEnabled(false), 500);
  };

  // 滚动开始时禁用动画
  const handleScrollStart = () => {
    setIsScrolling(true);
    setIsAnimationEnabled(false);
  };

  // 滚动结束后保持禁用状态
  const handleScrollEnd = () => {
    setIsScrolling(false);
  };

  const renderItem = (dayItem) => {
    const itemRepresents = mode === 'month' ? ITEM_REPRESENTS.MONTH : ITEM_REPRESENTS.WEEK;
    // key包含mode,确保切换视图时组件重新挂载,触发入场动画
    const itemKey = `${mode}_day_${dayItem.id}`;
    
    return (
      <MiniCalendarItem
        animationsEnabled={isAnimationEnabled && !isScrolling}
        key={itemKey}
        mode={mode}
        itemRepresents={itemRepresents}
        day={dayItem}
        weekIndex={dayItem.weekIndex}
      />
    );
  };

  return (
    <>
      <Button onPress={toggleCalendarMode} title="切换周/月视图" />
      <ScrollView
        onScroll={handleScrollStart}
        onScrollEndDrag={handleScrollEnd}
        onMomentumScrollEnd={handleScrollEnd}
      >
        {yourDayData.map(renderItem)}
      </ScrollView>
    </>
  );
};

步骤2:子组件根据状态条件加载动画

在MiniCalendarItem中,仅当animationsEnabled为true时才生成动画配置,否则传入undefined(Reanimated会自动跳过动画播放)。

import Animated, {
  FadeInDown,
  FadeInUp,
  SlideOutUp,
  SlideOutDown,
} from 'react-native-reanimated';

const MiniCalendarItem = ({ animationsEnabled, itemRepresents, day, weekIndex }) => {
  let animationEnter;
  let animationExit;

  // 仅在动画启用时生成动画配置
  if (animationsEnabled) {
    if (itemRepresents === ITEM_REPRESENTS.MONTH) {
      animationEnter = dayIsPartOfCurrentWeek(day) 
        ? FadeInUp 
        : FadeInUp.delay(weekIndex * 150).duration(350);
      animationExit = SlideOutDown.duration(400);
    } else {
      animationEnter = FadeInDown.duration(500);
      animationExit = SlideOutUp.duration(400);
    }
  } else {
    // 禁用动画,传入undefined跳过播放
    animationEnter = undefined;
    animationExit = undefined;
  }

  // 注意:dayIsPartOfCurrentWeek需要根据你的业务逻辑实现
  function dayIsPartOfCurrentWeek(day) {
    // 你的判断逻辑
    return true;
  }

  return (
    <Animated.View
      entering={animationEnter}
      exiting={animationExit}
    >
      {/* 日期内容渲染 */}
      <Text>{day.date}</Text>
    </Animated.View>
  );
};

关键细节说明

  1. key值包含mode:确保切换视图时,React会重新渲染所有日历项,触发入场动画;滚动时复用组件但动画已禁用,不会意外播放。
  2. 滚动事件监听:覆盖onScroll、onScrollEndDrag、onMomentumScrollEnd三个事件,确保滚动过程中始终禁用动画。
  3. 动画自动关闭:通过setTimeout根据最长动画时长设置自动关闭,避免动画开关一直处于开启状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 08:40:31