前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Flutter 动画篇 (Hero 动画)

Flutter 动画篇 (Hero 动画)

作者头像
心安事随
发布2024-12-29 09:28:26
发布2024-12-29 09:28:26
16300
代码可运行
举报
文章被收录于专栏:前端大合集前端大合集
运行总次数:0
代码可运行

Hero 动画

介绍

你可能经常遇到 hero 动画。比如,页面上显示的代售商品列表。选择一件商品后,应用会跳转至包含更多细节以及“购买”按钮的新页面。在 Flutter 中,图像从当前页面转到另一个页面称为 hero 动画,相同的动作有时也被称为 共享元素过渡。 引自-->. docs.flutter.cn/ui/animatio….

说白一点 就是, 同一个元素在不同页面之间的过渡动画.

场景

举两个案例:

  1. 从商品的简介, 点击商品之后 跳转到 商品的详细页面.
  2. 从外边的文章列表, 点击文章之后, 跳转到
  3. 从外部用户的头像,点击头像之后, 跳转到 用户的个人资料页

我们分析一下, 为什么在这种场景, 用hero 比较合适.

  1. 增强用户体验
    • 在页面切换过程中,通过 hero 动画,用户可以清晰地看到元素从一个页面平滑地过渡到另一个页面,这种视觉上的连贯性能够让用户更直观地理解两个页面之间的关联,减少认知负担,从而提升用户体验
  2. 突出关键元素
    • 无论是商品图片还是用户头像,都是页面中比较关键的元素。使用 hero 动画可以在页面转换时将用户的注意力集中在这些关键元素上,强调其重要性,引导用户进一步了解相关信息。
  3. 保持界面的一致性
    • 这种动画效果使得不同页面之间在元素过渡上保持一种统一的风格,不会让用户在页面跳转时感到突兀,有助于维护整个应用界面的一致性和整体性

场景模拟实现

我们主要拿从文章列表 跳转到文章详情页面, 过渡文章的封面图, 过渡内容 大小 和 位置.

效果:

WeChat_20241211223042.gif
WeChat_20241211223042.gif

仔细观察 我们就能看到 图片从外边到另外一个页面时,发生大小的变化 以及位置的偏移. 我们要实现起来也是非常的容易, 在这里我不讲 原理,只讲解如何使用的. 对原理实现感兴趣的大家可以去阅读这篇文章(docs.flutter.cn/ui/animatio…).

实现 1. 定义源 Hero 控件

  • 源 Hero:在第一个页面中创建一个 Hero widget。
  • 标签:为 Hero 指定一个唯一的 tag,用于识别这个共享元素。
  • 图形表示:通常是一个图像或图标,放置在当前显示的控件树中。
代码语言:javascript
代码运行次数:0
复制
Hero(
  tag: 'hero-tag',
  child: FlutterLogo(size: 100), // 源页面的图形表示
)

2. 定义目标 Hero 控件

  • 目标 Hero:在第二个页面中创建另一个 Hero widget。
  • 相同的标签:确保这个 Hero 使用与源 Hero 相同的 tag
  • 图形表示:通常是一个更大的图形,表示动画结束时的状态。
代码语言:javascript
代码运行次数:0
复制
Hero(
  tag: 'hero-tag',
  child: FlutterLogo(size: 200), // 目标页面的图形表示
)

3. 创建目标路由

  • 目标路由:定义一个新的页面(目标路由),其中包含目标 Hero。
  • 页面内容:在这个页面中,展示目标 Hero 的内容。
代码语言:javascript
代码运行次数:0
复制
class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Second Page')),
      body: Center(
        child: Hero(
          tag: 'hero-tag',
          child: FlutterLogo(size: 200), // 目标 Hero
        ),
      ),
    );
  }
}

4. 触发动画

  • 导航:通过 Navigator.push 方法将目标路由推送到导航堆栈。
  • 动画触发:当目标路由被推送时,Flutter 会自动处理源 Hero 和目标 Hero 之间的动画。
代码语言:javascript
代码运行次数:0
复制
Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => SecondPage()),
);

5. 动画过程

  • 动画计算:Flutter 会计算从源 Hero 到目标 Hero 的动画路径,包括位置和大小的变化。
  • 动画执行:在动画过程中,两个页面的 Hero 在叠加层中进行平滑过渡,给用户一种元素在页面之间移动的感觉。

相似的 Widget 树:为了获得最佳效果,源 Hero 和目标 Hero 应该有相似的 Widget 树结构。 唯一的 Tag:确保 tag 在整个应用中是唯一的,以避免冲突。

径向hero 动画

径向 Hero 动画是一种特殊类型的 Hero 动画,它通过从一个点向外扩展或收缩来创建视觉效果,通常用于在页面之间共享元素。与常规的 Hero 动画相比,径向 Hero 动画更注重从中心点向外的过渡效果。

  • 径向变换 将圆形动画 变为方形
  • 径向英雄动画在将英雄从源路线飞行到目标路线时执行径向变换
  • MaterialRectCenterArcTween定义补间动画
  • 使用PageRouteBuilder构建目标路线。

我们就以 官方的案例 进行演示讲解. docs.flutter.dev/ui/animatio…

RadialExpansion 类

代码语言:javascript
代码运行次数:0
复制
class RadialExpansion extends StatelessWidget {
  const RadialExpansion({
    super.key,
    required this.maxRadius,
    this.child,
  }) : clipRectSize = 2.0 * (maxRadius / math.sqrt2);

  final double maxRadius;
  final clipRectSize;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return ClipOval(
      child: Center(
        child: SizedBox(
          width: clipRectSize,
          height: clipRectSize,
          child: ClipRect(
            child: child, // Photo
          ),
        ),
      ),
    );
  }
}

构建底部四个 图表widget

代码语言:javascript
代码运行次数:0
复制
  Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: List.generate(4, (i) => _buildItem(context, i)),
            )

构建每一个item

代码语言:javascript
代码运行次数:0
复制
_buildItem(BuildContext context, int index) {
  const radius = 30;
  return CupertinoButton(
      child: SizedBox(
        width: radius * 2,
        height: radius * 2,
        child: Hero(
            tag: 'hero_tag_$index',
            createRectTween: (begin, end) {
              return MaterialRectCenterArcTween(begin: begin, end: end);
            },
            child: RadialExpansionWidget(
                maxRadius: 120,
                child: Container(
                    color: Colors.amber,
                    child: LayoutBuilder(builder: (context, constraints) {
                      return FlutterLogo(size: constraints.maxWidth);
                    })))),
      ),
      onPressed: () {
        Get.to(TargetPage(index: index));
      });
}

构建路由页的class

代码语言:javascript
代码运行次数:0
复制
class TargetPage extends StatelessWidget {
  final int index;

  const TargetPage({super.key, required this.index});

  @override
  Widget build(BuildContext context) {
    const maxRadius = 120.0;
    return GestureDetector(
      onTap: () => Navigator.of(context).pop(),
      child: Container(
        color: Theme.of(context).canvasColor,
        height: double.infinity,
        width: double.infinity,
        alignment: Alignment.center,
        child: Card(
          elevation: 8.0,
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              SizedBox(
                width: maxRadius * 2,
                height: maxRadius * 2,
                child: Hero(
                  tag: 'hero_tag_$index',
                  createRectTween: (begin, end) {
                    return MaterialRectCenterArcTween(begin: begin, end: end);
                  },
                  child: RadialExpansionWidget(
                    maxRadius: maxRadius,
                    child: Container(
                      color: Colors.red,
                      child: LayoutBuilder(
                        builder: (context, constraints) {
                          return FlutterLogo(size: constraints.maxWidth);
                        },
                      ),
                    ),
                  ),
                ),
              ),
              Text(
                '第$index个Item',
                style: const TextStyle(fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 16.0),
            ],
          ),
        ),
      ),
    );
  }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Hero 动画
    • 介绍
    • 场景
    • 场景模拟实现
    • 径向hero 动画
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档