RN 原生动画  Animated  用法、动画性能优化

0 阅读4分钟

React Native 原生 Animated 是 RN 官方提供的动画库,适合实现大部分基础动画(淡入淡出、缩放、位移、旋转等)。如果是面试,通常会问两个方面:

  • Animated 的基本用法
  • 动画为什么会卡?如何优化?

下面按这两个方面总结。


一、Animated 基本用法

1. 创建动画值

const fadeAnim = useRef(new Animated.Value(0)).current;

Animated.Value 是动画驱动值。

例如:

0 -----> 1

表示透明度从 0 到 1。


2. Animated.View

普通 View 不能绑定 Animated.Value。

必须使用:

<Animated.View
  style={{
    opacity: fadeAnim,
  }}
/>

3. timing 动画

最常见。

Animated.timing(fadeAnim, {
  toValue: 1,
  duration: 500,
  useNativeDriver: true,
}).start();

参数:

参数作用
toValue目标值
duration动画时间
easing缓动函数
delay延迟
useNativeDriver是否使用原生驱动

4. spring 弹簧动画

Animated.spring(scale, {
  toValue: 1,
  friction: 4,
  tension: 80,
  useNativeDriver: true,
}).start();

效果:

0.8
 ↓
1.1
 ↓
0.98
 ↓
1

适合:

  • Button点击
  • 卡片弹出
  • Drawer

5. decay

模拟惯性。

Animated.decay(position, {
  velocity: 2,
  deceleration: 0.998,
  useNativeDriver: true,
}).start();

一般用于:

滑动
↓

松手继续滑

6. sequence

串行动画。

Animated.sequence([
    Animated.timing(opacity,...),
    Animated.spring(scale,...)
]).start();

执行顺序:

透明
↓

缩放

7. parallel

并行动画。

Animated.parallel([
    Animated.timing(opacity,...),
    Animated.spring(scale,...)
]).start();

同时执行。


8. stagger

交错动画。

Animated.stagger(100, animations).start();

效果:

Item1

100ms

Item2

100ms

Item3

9. loop

循环动画。

Animated.loop(
    Animated.timing(...)
).start();

例如 Loading。


二、插值 interpolate

Animated.Value 往往只有

0~1

需要转换。

例如:

const rotate = value.interpolate({
    inputRange:[0,1],
    outputRange:["0deg","360deg"]
})

再绑定:

transform:[
    {
        rotate
    }
]

也可以:

0
↓

100px
translateX:value.interpolate({
    inputRange:[0,1],
    outputRange:[0,100]
})

三、Animated.event

绑定滚动。

const scrollY = useRef(new Animated.Value(0)).current;

<Animated.ScrollView
    onScroll={Animated.event(
        [{
            nativeEvent:{
                contentOffset:{
                    y:scrollY
                }
            }
        }],
        {
            useNativeDriver:true
        }
    )}
/>

这样 Header 就能跟随 Scroll 做动画,而且使用原生驱动时不会因 JS 线程繁忙而明显掉帧。(React Native)


四、Animated 常见动画

例如透明:

opacity

位移:

translateX
translateY

缩放:

scale

旋转:

rotate

组合:

transform:[
    {translateY},
    {scale},
    {rotate}
]

五、为什么 Animated 会卡?

RN 有:

JS Thread

↓

Bridge

↓

Native(UI Thread)

如果:

JS执行太久

例如:

大量计算

网络回调

render

setState

动画每一帧都要经过 Bridge 更新,就容易掉帧。


六、useNativeDriver 原理

默认:

JS

↓

计算动画

↓

Bridge

↓

Native

每一帧都要跨线程。

开启:

useNativeDriver:true

之后:

开始动画

↓

把动画配置发送给 Native

↓

Native 自己执行

动画运行过程中无需每帧经过 JS,因此即使 JS 线程短暂阻塞,动画仍能保持流畅。(React Native Archive)


七、为什么 useNativeDriver 更快?

因为:

没有 Bridge 通信

以前:

60FPS

↓

60Bridge

现在:

启动一次

↓

Native执行

Bridge 开销几乎没有。


八、useNativeDriver 的限制

只能动画:

opacity

transform

translate

scale

rotate

不能:

width

height

left

top

flex

margin

也就是说,布局属性通常不能由原生驱动直接动画。(React Native Archive)


九、动画性能优化

① 优先使用 useNativeDriver

Animated.timing(...,{
    useNativeDriver:true
})

这是官方推荐的首选优化。(React Native)


② 少动画 Layout

不要:

width

height

left

top

推荐:

translate

scale

opacity

GPU 对 transformopacity 的处理通常更高效。(React Native Archive)


③ 避免频繁 setState

错误:

setState()

↓

render()

↓

动画

改为:

Animated.Value

驱动 UI。


④ 减少组件重渲染

例如:

React.memo

useMemo

useCallback

动画组件不要反复 Render。


⑤ Scroll 动画使用 Animated.event

不要:

onScroll={()=>{
    setState()
}}

而是:

Animated.event(...)

减少 JS 参与,提高滚动跟随动画的流畅度。(React Native Archive)


⑥ 大列表不要全部动画

例如:

FlatList

1000 Item



1000 Animation

建议:

  • 仅为可见项添加动画
  • 配合 FlatList 的虚拟化能力
  • 避免长时间运行大量循环动画

⑦ 使用 Release 包测试

Debug 模式下:

JS很慢

不要拿 Debug 判断动画性能。


十、Animated 与 Reanimated 对比

对比项AnimatedReanimated
官方支持第三方(社区主流)
学习成本较高
useNativeDriver支持默认在 UI 线程执行工作单元
手势联动一般很强
复杂动画一般优秀
性能更好,尤其复杂交互场景

目前 React Native 社区中,如果涉及复杂手势、共享值、拖拽、布局动画等,很多项目更倾向于使用 Reanimated;而简单的透明度、位移、缩放等动画,官方 Animated 已经能够满足大多数需求。(SWMansion Docs)

面试总结(高频回答)

React Native 的 Animated 通过 Animated.Value 驱动动画,可使用 timingspringsequenceparallelloop 等 API 实现各种动画效果。性能优化的核心是尽量开启 useNativeDriver,让动画在原生 UI 线程执行,减少 JS 线程和 Bridge 的参与;同时优先动画 transformopacity,避免布局属性动画,减少组件重渲染,并在滚动场景使用 Animated.event 绑定事件,从而获得更流畅的 60 FPS 动画体验。(React Native)