从 RN 到 Flutter-动画

好的动画会使 UI 更加直观,是应用更加精致,并提高用户体验。Flutter 的动画使简单动画和复杂动画的实现变得容易。Flutter SDK 所遵守的 Material 设计语言包含了标准的动态效果,你可以简单地自定义它们并应用于你的应用。

在 React Native 中,Animated API 被应用于创建动画。

在 Flutter 中,使用Animation类和AnimationController类。Animation是包括一个动画初始状态到它终态(完成或者取消)状态的抽象类。AnimationController类控制动画的执行、反转或者停止动画,乃至于设置动画为某一个值以做到自定义的目的。

实现一个淡入动画

下面的 React Native 例子是利用 Animated API 创建的FadeInView组件。初始状态、终止状态已经过程都需要定义。该组件包裹需要动画的组件,透明值fadeAnim向下继承到Text组件中,使得执行start()后,动画就开始了。

// React Native
class FadeInView extends React.Component {
  state = {
    fadeAnim: new Animated.Value(0) // Initial value for opacity: 0
  };
  componentDidMount() {
    Animated.timing(this.state.fadeAnim, {
      toValue: 1,
      duration: 10000
    }).start();
  }
  render() {
    return (
      <Animated.View style={{...this.props.style, opacity: this.state.fadeAnim }} >
        {this.props.children}
      </Animated.View>
    );
  }
}
    ...
<FadeInView>
  <Text> Fading in </Text>
</FadeInView>
    ...

想在 Flutter 中实现相应效果,需要创建一个 AnimationController 对象,命名为controller并指明持续时间。默认AnimationController会在持续时间内线性执行动画。这个动画控制器在每一帧都会返回一个新值,默认是每秒 60 个值。

使用AnimationController时,必须传入vsync对象。引入vsync的目的是避免不在屏幕上的动画浪费资源。你可以使用你的状态机作为vsync,只要在类定义的时候混入TickerProviderStateMixin。一个AnimationController构造时需要一个 TickerProvider 作为vsync参数。

Tween描述初始值和结束值之间的插值。动画中使用Tween对象,可以将Tweenanimate()返回值传给要修改的Animation对象。

使用controller.forward()开始动画。其他操作如fling()repeat()也可以开始动画。比如下面的例子,将FlutterLogo部件放在FadeTransition部件中。

import 'package:flutter/material.dart';

void main() {
  runApp(Center(child: LogoFade()));
}

class LogoFade extends StatefulWidget {
  _LogoFadeState createState() => _LogoFadeState();
}

class _LogoFadeState extends State<LogoFade> with TickerProviderStateMixin {
  Animation animation;
  AnimationController controller;

  initState() {
    super.initState();
    controller = AnimationController(
        duration: const Duration(milliseconds: 3000), vsync: this);
    final CurvedAnimation curve =
    CurvedAnimation(parent: controller, curve: Curves.easeIn);
    animation = Tween(begin: 0.0, end: 1.0).animate(curve);
    controller.forward();
  }

  Widget build(BuildContext context) {
    return FadeTransition(
      opacity: animation,
      child: Container(
        height: 300.0,
        width: 300.0,
        child: FlutterLogo(),
      ),
    );
  }

  dispose() {
    controller.dispose();
    super.dispose();
  }
}

也可以把动画的实现封装成 hook。

Animation useFadeIn() {
  final ticker = useSingleTickerProvider();
  final controller = useMemoized(() => AnimationController(
      duration: const Duration(milliseconds: 3000), vsync: ticker));
  final animation = useMemoized(() => Tween(begin: 0.0, end: 1.0)
      .animate(CurvedAnimation(parent: controller, curve: Curves.easeIn)));

  useEffect(() {
    controller.forward();
    return () => controller.dispose();
  });

  return animation;
}
Android fadeIniOS fadeIn
Android fadeIniOS fadeIn

为卡片增加滑动关闭动画

在 React Native 里,可以使用如PanResponder或者第三方库实现华东关闭。

在 Flutter 中,为部件增加滑动动画可以使用Dismissible部件。

child: Dismissible(
  key: key,
  onDismissed: (DismissDirection dir) {
    cards.removeLast();
  },
  child: Container(
    ...
  ),
),
Android DismissableiOS dismissable
Android DissmisableiOS Dismissible

啊啊啊啊,这个系列翻译完了!!!后面还有一个 Flutter 和 React Native 组件的对照表格,我就不贴上来了。