啥?Flutter也能整3D了吗?我靠,竟然是这样的操作👀

简介: 当我看了这样一个节目之后,我发现,复杂的ui竟然这么简单就可以实现了!当时我就用Flutter整了这么个3D效果,快来围观!!
前言:在网上看到了一个3D展示的效果,属实强😎,于是我准备拿Flutter复刻一个,可是晚上找了半天也没有发现Flutter怎么使用3D模型,但是直到我看了这个节目,话不多说,先上效果图!

灵感来源:https://www.youtube.com/watch?v=FCyoHclCqc8&t=149s

视频讲解了很多复杂UI的处理制作

屏幕截图 2021-10-28 124641.jpg

效果图:

tt0.top-433400.gif

先分析一下原理,分为3个部分:

1.背景的旋转处理

2.文字的旋转动画处理

3.吉他(贝斯,写文章的时候才发现不是吉他)的旋转处理与吉他背景阴影的处理

1.背景的旋转处理

在复杂的动画中,常用的便是:Transform,Stack,AnimationController

这里也是一样:

tt0.top-133272.gif

_buildBackground() => Positioned.fill(
      top: -_extraHeight,
      bottom: -_extraHeight,
      child: AnimatedBuilder(
        animation: _animator,
        builder: (context, widget) => Transform.translate(
          offset: Offset(_maxSlide * _animator.value, 0),
          //重点是这里
          child: Transform(
            transform: Matrix4.identity() //单位矩阵,倾斜的角度
              ..setEntry(3, 2, 0.001)
              ..rotateY((pi / 2 + 0.1) * -_animator.value),
            alignment: Alignment.centerLeft, //相对于坐标系原点的对齐方式
            child: widget, 
          ),
        ),
        //以下就是普通的Widget
        child: Container(
          color: Color(0xffe8dfce),
          child: Stack(
            overflow: Overflow.visible,
            children: <Widget>[
              //Fender word
              Positioned(
                top: _extraHeight + 0.1 * _screen.height,
                left: 80,
                child: Transform.rotate(
                  angle: 90 * (pi / 180),
                  alignment: Alignment.centerLeft,
                  child: Text(
                    "HELLO",
                    style: TextStyle(
                      fontSize: 100,
                      color: Color(0xFFc7c0b2),
                      shadows: [
                        Shadow(
                          color: Colors.black26,
                          blurRadius: 5,
                          offset: Offset(2.0, 0.0),
                        ),
                      ],
                      fontWeight: FontWeight.w900,
                    ),
                  ),
                ),
              ),
              //给切换时加上一个黑色的背景动画,使动画更加立体
              AnimatedBuilder(
                animation: _animator,
                builder: (_, __) => Container(
                  color: Colors.black.withAlpha(
                    (150 * _animator.value).floor(),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );

2.文字的旋转动画处理

1635400145(1).png

  • 顶部文字

    //适配异形屏
    SafeArea(
          child: AnimatedBuilder(
              animation: _animator,
              builder: (_, __) {
                return Transform.translate(
                    //平移动画
                  offset: Offset((_screen.width - 60) * _animator.value, 0),
                  child: ...);
        )
    )
  • 底部文字

    这里的平移动画需要跟随背景动画,在此基础上加了一个透明度,一个平移动画,更加立体

    Opacity(
        //透明度
      opacity: 1 - _animator.value,
        //与背景动画相同,在其基础上加了一个平移动画
      child: Transform.translate(
        offset: Offset((_maxSlide + 50) * _animator.value, 0),
        child: Transform(
          transform: Matrix4.identity()
            ..setEntry(3, 2, 0.001)
            ..rotateY((pi / 2 + 0.1) * -_animator.value),
          alignment: Alignment.centerLeft,
          child: widget,
        ),
      ),
    )
  • 左侧文字

    此处的文字动画与上面相差不大,只在参数上有区别

    Transform.translate(
      offset: Offset(_maxSlide * (_animator.value - 1), 0),
      child: Transform(
        transform: Matrix4.identity()
          ..setEntry(3, 2, 0.001)
          ..rotateY(pi * (1 - _animator.value) / 2),
        alignment: Alignment.centerRight,
        child: widget,
      ),
    )

    3.贝斯的旋转处理与贝斯背景阴影的处理

这部分是本文的重点,先说一下3D效果的原理。我在这里找了120张贝斯的图,分别是每移动一点所对应的图片,当移动时,传入移动的距离,显示不同的图片

在这里封装了一个ImageSequenceAnimator,有个插件叫image_sequence_animator,但是功能不满足我自定义的需求,就把它的源码拿出来改了改。

注意事项:图片的命名

屏幕截图 2021-10-28 141224.jpg

在这里只给大家分析最重要的点了,源代码中我注释的很全~

屏幕截图 2021-10-28 141122.jpg

  • 对于图片名称的补齐:

    String _getSuffix(String value) {
      while (value.length < suffixCount) value = "0" + value;
      return value;
    }
  • 获取到图片的路径:

    String _getDirectory() {
      return folderName +
          "/" +
          fileName +
          _getSuffix((suffixStart + _previousFrame).toString()) +
          "." +
          fileFormat;
    }
  • UI处理:

    @override
    Widget build(BuildContext context) {
      if (widget.frame != null) {
        if (currentFrame == null ||
            widget.frame != _previousFrame ||
            colorChanged) {
          colorChanged = false;
          _previousFrame = widget.frame;
          if (_previousFrame < frameCount)
            currentFrame = Image.asset(
              _getDirectory(),
              color: color,
              gaplessPlayback: true,
            );
        }
        return currentFrame;
      }
      return ValueListenableBuilder(
        builder: (BuildContext context, int change, Widget cachedChild) {
          if (currentFrame == null ||
              animationController.value.floor() != _previousFrame ||
              colorChanged) {
            colorChanged = false;
            _previousFrame = animationController.value.floor();
            if (_previousFrame < frameCount)
              currentFrame = Image.asset(
                _getDirectory(),
                color: color,
                gaplessPlayback: true,
              );
          }
    //currentFrame是Image
          return currentFrame;
        },
        valueListenable: changeNotifier,
      );
    }
    ​
  • 使用方法:

    ImageSequenceAnimator(
      "assets/guitarSequence", //folderName
      "", //fileName
      1, //suffixStart
      4, //suffixCount
      "png", //fileFormat
      120, //frameCount
      fps: 60, 
      isLooping: false,
      isBoomerang: true,
      isAutoPlay: false,
      frame: (_objAnimator.value * 120).ceil(),
      // fullPaths: [(_objAnimator.value * 120).ceil().toString()],//官方插件的使用
    )
  • 贝斯阴影处理

    Positioned(
        //无需动画,用Stack叠在贝斯照片下面就行
      top: _extraHeight + 0.13 * _screen.height,
      bottom: _extraHeight + 0.24 * _screen.height,
      left: _maxSlide - 0.41 * _screen.width,
      right: _screen.width * 1.06 - _maxSlide,
      child: Column(
        children: <Widget>[
            //绘制了一个贝斯的形状
          Flexible(
            child: FractionallySizedBox(
              widthFactor: 0.2,
              child: Container(
                decoration: BoxDecoration(
                  boxShadow: [
                    BoxShadow(
                      blurRadius: 50,
                      color: Colors.black38,
                    )
                  ],
                  borderRadius: BorderRadius.circular(50),
                ),
              ),
            ),
          ),
          Flexible(
            child: Container(
              decoration: BoxDecoration(
                boxShadow: [
                  BoxShadow(
                    blurRadius: 50,
                    color: Colors.black26,
                  )
                ],
                borderRadius: BorderRadius.circular(50),
              ),
            ),
          ),
        ],
      ),
    ),

Flutter复杂的3D就这样很简单就完成啦~

相关文章
|
索引
【Flutter】底部导航栏页面框架 ( BottomNavigationBar 底部导航栏 | PageView 滑动页面 | 底部导航与滑动页面关联操作 )(一)
【Flutter】底部导航栏页面框架 ( BottomNavigationBar 底部导航栏 | PageView 滑动页面 | 底部导航与滑动页面关联操作 )(一)
360 0
|
Dart
【Flutter】HTTP 网络操作 ( 引入 http 插件 | 测试网站 | Get 请求 | Post 请求 | 将响应结果转为 Dart 对象 | Future 异步调用 )(二)
【Flutter】HTTP 网络操作 ( 引入 http 插件 | 测试网站 | Get 请求 | Post 请求 | 将响应结果转为 Dart 对象 | Future 异步调用 )(二)
288 0
【Flutter】HTTP 网络操作 ( 引入 http 插件 | 测试网站 | Get 请求 | Post 请求 | 将响应结果转为 Dart 对象 | Future 异步调用 )(二)
|
存储 JSON Dart
【Flutter】HTTP 网络操作 ( 引入 http 插件 | 测试网站 | Get 请求 | Post 请求 | 将响应结果转为 Dart 对象 | Future 异步调用 )(一)
【Flutter】HTTP 网络操作 ( 引入 http 插件 | 测试网站 | Get 请求 | Post 请求 | 将响应结果转为 Dart 对象 | Future 异步调用 )(一)
455 0
【Flutter】HTTP 网络操作 ( 引入 http 插件 | 测试网站 | Get 请求 | Post 请求 | 将响应结果转为 Dart 对象 | Future 异步调用 )(一)
|
Dart 开发者
【Flutter】底部导航栏页面框架 ( BottomNavigationBar 底部导航栏 | PageView 滑动页面 | 底部导航与滑动页面关联操作 )(二)
【Flutter】底部导航栏页面框架 ( BottomNavigationBar 底部导航栏 | PageView 滑动页面 | 底部导航与滑动页面关联操作 )(二)
163 0
【Flutter】底部导航栏页面框架 ( BottomNavigationBar 底部导航栏 | PageView 滑动页面 | 底部导航与滑动页面关联操作 )(二)
|
Dart Java
flutter中list相关操作汇总(有这一篇就够啦)
要说,List在我的开发使用中,确实是最为频繁的了,那么如何使用list,也就成了一个问题,list提供的方法又有哪些 这些都是需要掌握理解的。
2154 0
|
2月前
|
Android开发 iOS开发 容器
鸿蒙harmonyos next flutter混合开发之开发FFI plugin
鸿蒙harmonyos next flutter混合开发之开发FFI plugin
|
1月前
|
开发框架 Dart 前端开发
Flutter 是谷歌推出的一款高效跨平台移动应用开发框架,使用 Dart 语言,具备快速开发、跨平台支持、高性能、热重载及美观界面等特点。
Flutter 是谷歌推出的一款高效跨平台移动应用开发框架,使用 Dart 语言,具备快速开发、跨平台支持、高性能、热重载及美观界面等特点。本文从 Flutter 简介、特点、开发环境搭建、应用架构、组件详解、路由管理、状态管理、与原生代码交互、性能优化、应用发布与部署及未来趋势等方面,全面解析 Flutter 技术,助你掌握这一前沿开发工具。
56 8
|
1月前
|
存储 JavaScript 前端开发
在Flutter开发中,状态管理至关重要。随着应用复杂度的提升,有效管理状态成为挑战
在Flutter开发中,状态管理至关重要。随着应用复杂度的提升,有效管理状态成为挑战。本文介绍了几种常用的状态管理框架,如Provider和Redux,分析了它们的基本原理、优缺点及适用场景,并提供了选择框架的建议和使用实例,旨在帮助开发者提高开发效率和应用性能。
36 4
|
1月前
|
传感器 前端开发 Android开发
在 Flutter 开发中,插件开发与集成至关重要,它能扩展应用功能,满足复杂业务需求
在 Flutter 开发中,插件开发与集成至关重要,它能扩展应用功能,满足复杂业务需求。本文深入探讨了插件开发的基本概念、流程、集成方法、常见类型及开发实例,如相机插件的开发步骤,同时强调了版本兼容性、性能优化等注意事项,并展望了插件开发的未来趋势。
42 2
|
2月前
|
开发者
鸿蒙Flutter实战:07-混合开发
鸿蒙Flutter混合开发支持两种模式:1) 基于har包,便于主项目开发者无需关心Flutter细节,但不支持热重载;2) 基于源码依赖,利于代码维护与热重载,需配置Flutter环境。项目结构包括AppScope、flutter_module等目录,适用于不同开发需求。
109 3