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

React Native中如何为映射生成的元素依次添加逐个触发的动画

问题背景

我通过映射对象数组生成标签元素,将数组明细数据绑定到对应元素上,已实现渲染时所有标签同步放大至完整尺寸的动画效果。现在希望优化效果,让每个标签按顺序单独触发动画,即标签逐个依次加载动画,属于常见的动画使用场景,想知道基于现有代码的实现方法,以及相关的通用实现方案。

原有相关代码

import {LeftIconsRightText} from '@atoms/LeftIconsRightText';
import {LeftTextRightCircle} from '@atoms/LeftTextRightCircle';
import {Text, TextTypes} from '@atoms/Text';
import VectorIcon, {vectorIconTypes} from '@atoms/VectorIcon';
import styled from '@styled-components';
import * as React from 'react';
import {useEffect, useRef} from 'react';
import {Animated, ScrollView} from 'react-native';

export interface ICustomerFeedbackCard {
  title: string;
  titleIconName: string[];
  tagInfo?: {feedback: string; rating: number}[];
}

export const CustomerFeedbackCard: React.FC<ICustomerFeedbackCard> = ({
  title,
  titleIconName,
  tagInfo,
  ...props
}) => {
  const FAST_ZOOM = 800;
  const START_ZOOM_SCALE = 0.25;
  const FINAL_ZOOM_SCALE = 1;
  const zoomAnim = useRef(new Animated.Value(START_ZOOM_SCALE)).current;

  /**
   * Creates an animation with a
   * set duration and scales the
   * size by a set factor to create
   * a small zoom effect
   */
  useEffect(() => {
    const zoomIn = () => {
      Animated.timing(zoomAnim, {
        toValue: FINAL_ZOOM_SCALE,
        duration: FAST_ZOOM,
        useNativeDriver: true,
      }).start();
    };
    zoomIn();
  }, [zoomAnim]);

  /**
   * Sorts all tags from highest
   * to lowest rating numbers
   * @returns void
   */

  const sortTags = () => {
    tagInfo?.sort((a, b) => b.rating - a.rating);
  };

  /**
   * Displays the all the created tags with
   * the feedback text and rating number
   * @returns JSX.Element
   */
  const displayTags = () =>
    tagInfo?.map((tag) => (
      <TagContainer
        style={[
          {
            transform: [{scale: zoomAnim}],
          },
        ]}>
        <LeftTextRightCircle feedback={tag.feedback} rating={tag.rating} />
      </TagContainer>
    ));

  return (
    <CardContainer {...props}>
      <HeaderContainer>
        <LeftIconsRightText icons={titleIconName} textDescription={title} />
        <Icon name="chevron-right" type={vectorIconTypes.SMALL} />
      </HeaderContainer>
      <ScrollOutline>
        <ScrollContainer>
          {sortTags()}
          {displayTags()}
        </ScrollContainer>
      </ScrollOutline>
      <FooterContainer>
        <TextFooter>Most recent customer compliments</TextFooter>
      </FooterContainer>
    </CardContainer>
  );
};

参考对象数组

export const FEEDBACKS = [
  {feedback: 'Good Service', rating: 5},
  {feedback: 'Friendly', rating: 2},
  {feedback: 'Very Polite', rating: 2},
  {feedback: 'Above & Beyond', rating: 1},
  {feedback: 'Followed Instructions', rating: 1},
  {feedback: 'Speedy Service', rating: 3},
  {feedback: 'Clean', rating: 4},
  {feedback: 'Accommodating', rating: 0},
  {feedback: 'Enjoyable Experience', rating: 10},
  {feedback: 'Great', rating: 8},
];
通用实现方案(基于React Native内置Animated)

原来的同步动画问题在于所有标签共用了同一个动画值,要实现逐个动画只需要给每个标签分配独立的动画值,按索引设置启动延迟即可,修改后的核心代码如下:

// 替换原来的单个动画值定义
const zoomAnims = useRef<Animated.Value[]>([]).current;

useEffect(() => {
  // 先按标签数量初始化动画值数组
  zoomAnims.length = 0;
  tagInfo?.forEach(() => {
    zoomAnims.push(new Animated.Value(START_ZOOM_SCALE));
  });

  // 批量创建带延迟的动画,按索引依次延迟200ms启动,可自行调整间隔时长
  const animations = zoomAnims.map((anim, index) => 
    Animated.timing(anim, {
      toValue: FINAL_ZOOM_SCALE,
      duration: FAST_ZOOM,
      delay: index * 200,
      useNativeDriver: true,
    })
  );
  // 并行启动所有动画,因为已经加了延迟,自然会按顺序执行
  Animated.parallel(animations).start();
}, [tagInfo]);

const displayTags = () =>
  tagInfo?.map((tag, index) => (
    <TagContainer
      key={tag.feedback} // 记得加唯一key
      style={[
        {
          transform: [{scale: zoomAnims[index]}],
        },
      ]}>
      <LeftTextRightCircle feedback={tag.feedback} rating={tag.rating} />
    </TagContainer>
  ));

该方案不需要引入额外依赖,是React Native原生动画的标准实现逻辑。

最终采用的简化方案

通过react-native-animatable库实现的方案更简洁,库内置了动画延迟属性,直接通过索引计算延迟时间即可实现效果,核心代码如下:

const displayTags = () =>
    tagInfo?.map((tag, index) => (
      <TagContainer animation="zoomIn" duration={1000} delay={index * 1000}>
        <LeftTextRightCircle feedback={tag.feedback} rating={tag.rating} />
      </TagContainer>
    ));

内容的提问来源于stack exchange,提问作者Ibrahim Noor Mohammed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 11:24:02