首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Flutter Dojo设计之道——骚气的闪屏动画是如何实现的

Flutter Dojo设计之道——骚气的闪屏动画是如何实现的

作者头像
用户1907613
发布2020-08-06 21:34:19
1.2K0
发布2020-08-06 21:34:19
举报
文章被收录于专栏:Android群英传Android群英传

这篇文章是对Flutter动画实现思路的一篇剖析,用一个简单的动画,分析Flutter创建动画的一般步骤

闪屏,实际上有两个作用。

  • 宣传。通过Logo、广告等形式,在启动时,展示要宣传的广告等内容。
  • 后台初始化。借助这个时间做一些后台操作,初始化一些SDK或者代码。

Flutter Dojo的闪屏动画,参考了著名大厂——P站的App闪屏,相信大家应该都不陌生。

动画其实比较简单,只是一个从两边向中间靠拢的动画。

一般来说,Flutter的动画创作,有下面几个步骤。

  1. 创建静态布局
  2. 创建Tween,标记动画的起始值
  3. 给静态代码添加AnimatedBuilder,驱动动画

静态布局

这个布局没有什么太大难度,这个效果其实有很多实现方案,比如Center-Row的方式,让【Flutter】Text和【Dojo】Text在Row中居中即可。或者可以用Stack-Positioned的方式,通过left、right来定位。

相比来说,Center-Row的方式会比较直观,所以我这里准备使用Stack-Positioned的方式来进行演示。

不管使用哪种方案,需要注意的一点是,【Flutter】Text和【Dojo】Text是整体居中的,并不是分别居中,因为【Flutter】Text比【Dojo】Text要长,所以沿屏幕中线居中会很不协调。

布局之外,需要稍微提下【Dojo】Text的实现,实际上就是通过BoxDecoration来实现的,代码如下所示。

decoration: BoxDecoration(
  borderRadius: BorderRadius.circular(8),
  color: Color.fromARGB(255, 253, 152, 39),
),

定义动画

这里的动画分为两部分,左边和右边,如果使用Center-Row的方式,由于两个Text并不在屏幕中线对齐,所以实际上是有个offset的,然后再通过Transform.translate来进行偏移。另一种方式,Stack-Positioned实际上也是如此,但是可以通过Positioned中的left和right来进行动画。

所以首先一步,需要获取【Flutter】Text和【Dojo】Text的宽度差,这里又有多种方式来获取一个Widget的Size了。

  • LayoutBuilder。由于需要提前创建动画,所以这个方案不是很好。
  • TextPainter。对于文字,可以使用TextPainter来进行文本的测量。
  • Key。通过Key来获取RenderBox,从而获取Widget的Size。

Key的方式比较简单,所以这里我准备用TextPainter的方式来演示。下面这个函数就演示了如何获取一个特定TextStyle下Text的计算宽度。

double getTextWidth(String text) {
  final textPainter = TextPainter(
    text: TextSpan(
      text: text,
      style: TextStyle(
        fontSize: 60,
        fontWeight: FontWeight.w600,
      ),
    ),
    textDirection: TextDirection.ltr,
  );
  textPainter.layout(minWidth: 0, maxWidth: double.infinity);
  return textPainter.width;
}

经过简单的计算,【Flutter】Text和【Dojo】Text偏移的值实际上就是两个文本的宽度差的一半。

由于前面使用的是Stack-Positioned的方式进行的布局,所以动画也需要根据静态布局来定义。

先看【Flutter】Text的动画,它从屏幕左边作用到中间带偏移的地方,所以其动画值的范围是:

begin: screenWidth, end: screenWidth / 2 - offset

相应的,【Dojo】Text的动画,也类似:

begin: screenWidth, end: screenWidth / 2 + offset

动画管理

在确定的动画值的范围之后,实际上Tween就已经确定了,这里介绍一个动画管理的技巧,通过一个类来封装Widget所需要的不同的Tween,这样可以将动画的逻辑和Widget进行解耦,代码如下所示。

import 'package:flutter/material.dart';

class SplashAnimManager {
  final AnimationController controller;
  final Animation<double> animLeft;
  final Animation<double> animRight;
  final double screenWidth;
  final double offset;

  SplashAnimManager(this.controller, this.screenWidth, this.offset)
      : animLeft = Tween(begin: screenWidth, end: screenWidth / 2 - offset).animate(
          CurvedAnimation(
            parent: controller,
            curve: Curves.easeIn,
          ),
        ),
        animRight = Tween(begin: screenWidth, end: screenWidth / 2 + offset).animate(
          CurvedAnimation(
            parent: controller,
            curve: Curves.easeIn,
          ),
        );
}

这里仅仅是为了演示这种动画管理的思想,才将仅仅两个动画写在了管理类中。

实际上Flutter Dojo中有很多地方都是这样,不仅仅可以从App上学习Flutter的相关知识,通过阅读Dojo的源码,你会发现更多。

动画组装

最后就是通过AnimatedBuilder来进行组装,动画的本质实际上就是不断修改某个属性的值,从而产生动画的效果。【Flutter】Text和【Dojo】Text也是一样,以【Flutter】Text为例,实际上就是right属性从Tween的begin到end进行变化,所以,给静态布局套上AnimatedBuilder,再给相应的属性设置Tween的值就可以了,代码如下所示。

@override
Widget build(BuildContext context) {
  final double screenWidth = MediaQuery.of(context).size.width;
  _splashAnimManager = SplashAnimManager(
    _animationController,
    screenWidth,
    (getTextWidth('Flutter') - getTextWidth('Dojo') - 4) / 2,
  );
  return Container(
    alignment: Alignment.center,
    color: Colors.black,
    child: Stack(
      fit: StackFit.expand,
      alignment: Alignment.center,
      children: <Widget>[
        AnimatedBuilder(
          animation: _animationController,
          builder: (context, widget) {
            return Positioned(
              right: _splashAnimManager.animLeft.value,
              child: Text(
                'Flutter',
                style: TextStyle(
                  fontSize: 60,
                  color: Colors.white,
                  fontWeight: FontWeight.w600,
                ),
              ),
            );
          },
        ),
        AnimatedBuilder(
          animation: _animationController,
          builder: (context, widget) {
            return Positioned(
              left: _splashAnimManager.animRight.value,
              child: Container(
                padding: EdgeInsets.symmetric(
                  horizontal: 4,
                ),
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(8),
                  color: Color.fromARGB(255, 253, 152, 39),
                ),
                child: Text(
                  'Dojo',
                  style: TextStyle(
                    fontSize: 60,
                    fontWeight: FontWeight.w600,
                    color: Colors.black,
                  ),
                ),
              ),
            );
          },
        ),
      ],
    ),
  );
}

以上,一个骚气的闪屏动画就完成了。

代码地址

https://github.com/xuyisheng/flutter_dojo/blob/master/lib/pages/splash/slpash.dart 修仙

Flutter Dojo开源至今,受到了很多Flutter学习者和爱好者的喜爱,也有越来越多的人加入到Flutter的学习中来,所以我建了个Flutter修仙群,但是人数太多,所以分成了【Flutter修仙指南】【Flutter修仙指北】【Flutter修仙指东】三个群,对Flutter感兴趣的朋友,可以添加我的微信,注明加入Flutter修仙群,或者直接关注我的微信公众号【Android群英传】。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-08-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 群英传 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 静态布局
  • 定义动画
  • 动画管理
  • 动画组装
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档