Flutter 实现背景 Parallax 动画

news2024/11/18 13:31:05

Flutter 实现背景 Parallax 动画

alt

原文 https://arkapp.medium.com/background-parallax-animation-in-flutter-4aa9e23d6cfb

前言

alt

我们将创建我们的 Flutter 项目惊人的 Parallax 动画。

在本文中,我们将实现一个简单的实用工具 widget ,它将在任何 widget 之上添加 Parallax 效果。

正文

创建 Base Widget

让我们创建我们的基础 widget ,我们将添加 Parallax 动画。在 BaseWidget 中,我们将从 Asset 目录添加一个图像。稍后,我们将添加 Parallax 效果到这个图像。

import 'package:flutter/material.dart';

class BaseWidget extends StatelessWidget {
  const BaseWidget({super.key});

  @override
  Widget build(BuildContext context) {

    ///We will add parallax effect to this image
    return Image.asset(
      'assets/moon.webp',
      width: MediaQuery.of(context).size.width,
      height: MediaQuery.of(context).size.height,
    );
  }
}

创建 Parallax widget

现在我们将创建一个实用工具 widget ,它将为上面的 BaseWidget 添加 Parallax 效果。这将是采用子窗口 widget 作为构造函数参数的状态窗口 widget 。

import 'package:flutter/material.dart';

class ParallaxAnimationWidget extends StatefulWidget {
  final Widget child;

  const ParallaxAnimationWidget({required this.child, super.key});

  @override
  State<ParallaxAnimationWidget> createState() => _WidgetState();
}

class _WidgetState extends State<ParallaxAnimationWidget{


  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

现在我们将增加子窗口 widget 的宽度。为此,我们将首先确定子 widget 的宽度。我们将把 GlobalKeyto 添加到子 widget 中,使用该键将获取 widget 的宽度。

import 'package:flutter/material.dart';

class ParallaxAnimationWidget extends StatefulWidget {
  final Widget child;

  const ParallaxAnimationWidget({required this.child, super.key});

  @override
  State<ParallaxAnimationWidget> createState() => _WidgetState();
}

class _WidgetState extends State<ParallaxAnimationWidget{

  final childKey = GlobalKey();

  late Widget childWithKey;
  double? childBaseWidth;

  @override
  void initState() {
    super.initState();

    childWithKey = SizedBox(key: childKey, child: widget.child);
    fetchChildWidth();

  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        childWithKey,
      ],
    );
  }

  fetchChildWidth() {
    WidgetsBinding.instance.addPostFrameCallback(
      (_) {
        final RenderBox renderBoxRed =
            childKey.currentContext?.findRenderObject() as RenderBox;
        final childSize = renderBoxRed.size;
        childBaseWidth = childSize.width;
      },
    );
  }

}

这里我们添加了 StackWidget,然后在其中添加了子 Widget。现在,我们将增加我们的子窗口 widget 的宽度,以实现横跨该宽度的 Parallax 效果。

import 'package:flutter/material.dart';

class ParallaxAnimationWidget extends StatefulWidget {
  final Widget child;

  const ParallaxAnimationWidget({required this.child, super.key});

  @override
  State<ParallaxAnimationWidget> createState() => _WidgetState();
}

class _WidgetState extends State<ParallaxAnimationWidget{

  ///We are increasing the widget width by 20% you can change according
  ///to your needs.
  final parallaxWidthPercent = 20;
  final childKey = GlobalKey();

  late Widget childWithKey;
  double? childBaseWidth;
  double? totalAdditionalParallaxWidth;
  double? rightPosition;
  double maxEndPosition = 0;
  double maxStartPosition = 0;

  @override
  void initState() {
    super.initState();

    childWithKey = SizedBox(key: childKey, child: widget.child);
    fetchChildWidth();
  }

  @override
  Widget build(BuildContext context) {

    return Stack(
      children: [
        ///Validating the width and setting new increased width.
        SizedBox(
            width:
                (childBaseWidth != null && totalAdditionalParallaxWidth != null)
                    ? (childBaseWidth! + totalAdditionalParallaxWidth!)
                    : null,
            child: childWithKey,
          ),
      ],
    );
  }


  fetchChildWidth() {
    WidgetsBinding.instance.addPostFrameCallback(
      (_) {
        final RenderBox renderBoxRed =
            childKey.currentContext?.findRenderObject() as RenderBox;
        final childSize = renderBoxRed.size;
        childBaseWidth = childSize.width;
        initChildPosition();
      },
    );
  }

  initChildPosition() {
    totalAdditionalParallaxWidth = childBaseWidth! * parallaxWidthPercent / 100;
    rightPosition = -totalAdditionalParallaxWidth! / 2;
    maxEndPosition = -childBaseWidth! + totalAdditionalParallaxWidth!;
    maxStartPosition = totalAdditionalParallaxWidth!;
    setState(() {});
  }
}

我们已经增加了 20% 的宽度部件,您可以根据您的需要改变。我们还使用新的宽度计算了动画位置参数(right position、 maxEndposition、 maxStartposition)。这个新参数将在下一步中用于添加动画。

添加动画

我们将使用 Animatedposition 来创建美丽的 Parallax 动画。我们将创建一个定时器,它将不断改变我们的子窗口 widget 的位置,以创建 Parallax 效果。

import 'dart:async';
import 'package:flutter/material.dart';

class ParallaxAnimationWidget extends StatefulWidget {
  final Widget child;

  const ParallaxAnimationWidget({required this.child, super.key});

  @override
  State<ParallaxAnimationWidget> createState() => _WidgetState();
}

class _WidgetState extends State<ParallaxAnimationWidget{

  ///You can change the duration accoridng to your needs
  Duration animationDuration = const Duration(seconds: 15);
  final initialDelay = Future.delayed(const Duration(seconds: 1));
  Timer? animationTimer;

  final parallaxWidthPercent = 20;
  final childKey = GlobalKey();

  late Widget childWithKey;
  double? childBaseWidth;
  double? totalAdditionalParallaxWidth;
  double? rightPosition;
  double maxEndPosition = 0;
  double maxStartPosition = 0;

  @override
  void initState() {
    super.initState();
    childWithKey = SizedBox(key: childKey, child: widget.child);
    fetchChildWidth();
    initTimer();
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [

        ///This will animate our widget between edge positions.
        AnimatedPositioned(
          right: rightPosition,
          duration: animationDuration,
          child: SizedBox(
            width:
                (childBaseWidth != null && totalAdditionalParallaxWidth != null)
                    ? (childBaseWidth! + totalAdditionalParallaxWidth!)
                    : null,
            child: childWithKey,
          ),
        ),
      ],
    );
  }

  initTimer() async {
    animationTimer?.cancel();
    await initialDelay;
    updateChildPosition();
    animationTimer = Timer.periodic(
      animationDuration,
      (_) => updateChildPosition(),
    );
  }

  ///This method will animate our widget horizontally
  updateChildPosition() async {
    if (rightPosition == 0) {
      rightPosition = maxEndPosition;
    } else if (rightPosition == maxEndPosition) {
      rightPosition = maxStartPosition;
    } else if (rightPosition == maxStartPosition) {
      rightPosition = maxEndPosition;
    } else {
      rightPosition = maxEndPosition;
    }
    setState(() {});
  }

  fetchChildWidth() {
    WidgetsBinding.instance.addPostFrameCallback(
      (_) {
        final RenderBox renderBoxRed =
            childKey.currentContext?.findRenderObject() as RenderBox;
        final childSize = renderBoxRed.size;
        childBaseWidth = childSize.width;
        initChildPosition();
      },
    );
  }

  initChildPosition() {
    totalAdditionalParallaxWidth = childBaseWidth! * parallaxWidthPercent / 100;
    rightPosition = -totalAdditionalParallaxWidth! / 2;
    maxEndPosition = -childBaseWidth! + totalAdditionalParallaxWidth!;
    maxStartPosition = totalAdditionalParallaxWidth!;
    setState(() {});
  }

  @override
  void dispose() {
    super.dispose();

    ///Closing timer on widget dispose.
    animationTimer?.cancel();
  }
}

使用 Parallax 动画

我们已经实现了 ParallaxAnimationWidget,现在我们只需要将它添加到 BaseWidget 中就可以看到它的神奇之处了。

import 'package:parallax/base_widget.dart';
import 'package:parallax/parallax_animation_widget.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {

    return const MaterialApp(

      ///Addding animation to our base widget
      home: ParallaxAnimationWidget(
        child: BaseWidget(),
      ),
    );
  }
}

就是这样!您已经成功地添加 Parallax 动画到您的 Flutter 项目。您可以在项目中的任何地方使用此 widget 来创建令人惊叹的 UI。

alt

结束语

如果本文对你有帮助,请转发让更多的朋友阅读。

也许这个操作只要你 3 秒钟,对我来说是一个激励,感谢。

祝你有一个美好的一天~


© 猫哥

  • 微信 ducafecat

  • https://wiki.ducafecat.tech

  • https://video.ducafecat.tech

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/29832.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Transformer/Bert

诸神缄默不语-个人CSDN博文目录 文章目录1. Transformer1.1 Transformer整体工作流程1.2 Transformer的输入1.2.1 单词 Embedding1.2.2 位置 Embedding1.3 Block1.3.1 Add & Norm层1.3.2 Self-Attention1.3.3 Multi-Head Attention1.3.4 Feed Forward1.3.5 Masked Multi-He…

[附源码]计算机毕业设计JAVA篮球装备商城系统

[附源码]计算机毕业设计JAVA篮球装备商城系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybati…

HTTP/2是什么?和HTTP/1.1有什么不同?和SPDY有什么不同?

HTTP/2简介 HTTP/2 是超文本传输协议第2版&#xff0c;最初命名为 HTTP 2.0 &#xff0c;其简称为 h2&#xff08;基于TLS/1.2或以上版本的加密连接&#xff09;或 h2c&#xff08;非加密连接&#xff09;&#xff0c;是HTTP协议的的第二个主要版本&#xff0c;使用于万维网。 …

备考2023年软考需要了解什么?

2022年软考已经结束啦&#xff01;下半年还是有不少地区取消了&#xff0c;没能报上名或是没能正常参考的朋友们&#xff0c;只能把目光转向2023年了。 这篇文章就来给大家讲讲备考2023年软考需要了解什么&#xff1f; 软考小白必看哦&#xff01; 一、软考是啥&#xff1f;…

带你走进神奇的元宇宙的世界

&#x1f3e0;个人主页&#xff1a;黑洞晓威 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晓威&#xff0c;一名普普通通的大二在校生&#xff0c;希望在CSDN中与大家一起成长。&#x1f381;如果你也在正在学习Java&#xff0c;欢迎各位大佬来到我的博客查漏补缺…

阿里架构师推荐,微服务分布式构架开发实战PDF,快快收藏吧

什么是微服务架构 微服务是一种软件架构风格&#xff0c;目标是将一个复杂的应用拆分成多个服务模块&#xff0c;每个模块专注单一业务功能对外提供服务&#xff0c;并可以独立编译及部署&#xff0c;同时各模块间互相通信彼此协作&#xff0c;组合为整体对外提供完整服务。 微…

嵌入式开发学习之--创建工程

提示&#xff1a;本篇文章依旧是了解为主&#xff0c;实际做项目时直接找开源的类似项目做模板更效率。 文章目录一、前言二、新建工程三、使用模板工程四、总结一、前言 前面的学习&#xff0c;都是文件内的代码联系&#xff0c;这一篇&#xff0c;是学习文件与目录的联系。当…

数字信号处理-8-自相关

1 皮尔森相关系数 假设 x 和 y 均为 N 个样本的数组&#xff0c;皮尔森公式如下&#xff1a; 皮尔森相关系数总是在 -1 到 1 之间&#xff08;包含这两个字&#xff09;。ρ 的绝对值意味着相关性的强度。ρ 接近 1 表示强正相关&#xff1b;ρ 接近 -1 表示强负相关&#xf…

上传文件很费时费力?那是你没用对方式

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、前端直传的优点二、实现步骤2.1、后端方面2.1.1 添加依赖2.1.2 增加接口2.1.3 测试接口2.2、前端方面2.2.1 安装 cos-js-sdk-v5 依赖2.2.2 新建组件2.2.3 使…

你的哪些SQL慢?看看MySQL慢查询日志吧

前言 在项目里面&#xff0c;多多少少都隐藏着一些执行比较慢的SQL, 不同的开发测试人员在平时使用的过程中多多少少都能够遇到&#xff0c;但是无法立马有时间去排查解决。那么如果有一个文件能够将这些使用过程中比较慢的SQL记录下来&#xff0c;定期去分析排查&#xff0c;…

meta视口标签

属性解释说明width宽度设置的是viewport宽度&#xff0c;可以设置device-width特殊值initial-scale初始缩放比&#xff0c;大于0的数字maximum-scale最大缩放比&#xff0c;大于0的数字minimum-scale最小缩放比&#xff0c;大于0的数字user-scalable用户是否可以缩放&#xff0…

关于我们编写好的java程序是如何运行部署的

了解如何去查看项目如何运行怎么部署java项目部署到服务器的程序和在本地运行的程序有什么不同java中的Class文件是如何形成的Class文件如何执行的怎么部署java项目 首先宏观的说一下&#xff0c;程序的运行都是要有一个启动入口的&#xff0c;也就是我们经常说的main函数是程…

【Shell 脚本速成】03、Shell 脚本实战案例(一)数据磁盘初始化

目录 一、案例应用场景 二、案例需求 脚本所需相关知识点 三、案例算法 四、代码实现 五、实现验证 一、案例应用场景 生产环境中的服务器一般会分为系统盘和数据盘两种磁盘&#xff0c;以dell R730举例&#xff0c;该服务器是一个2U的机架式服务器&#xff0c;满载可以挂…

HTML+CSS简单的网页制作期末作业——浙江旅游景点介绍网页制作

家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法&#xff0c;如盒子的嵌套、浮动、margin、border、background等属性的使用&#xff0c;外部大盒子设定居中&#xff0c;内部左中右布局&#xff0c;下方横向浮动排列&#xff0c;大学学习的前端知识点和布局方式都有…

Redisson分布锁原理分析及源码解读

本文源码解读基于Redisson 3.18.0 版本 Redisson分布锁实现原理 Redisson锁实现基本原理大致如下图所示&#xff1a; 客户端执行Lua脚本去获取锁&#xff0c;如果获取失败&#xff0c;则订阅解锁消息&#xff0c;并挂起线程。 客户端解锁时执行一段Lua脚本&#xff0c;删除锁的…

[附源码]计算机毕业设计JAVA课程资源系统管理

[附源码]计算机毕业设计JAVA课程资源系统管理 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybati…

PowerJob 定时从SFTP下载文件拆的坑

一. 业务需求 SFTP上有多个目录, 每小时要下载一次文件, 每个目录的下载任务都是一个独立的工作流任务. 二.问题描述 手动执行每个任务可以正常执行, 但是当所有任务都开启定定时任务执行时(每小时执行一次),任务实例就会报错. 三.问题分析 查看服务端和worker端的日志, …

数据采集-“消防知识网上答题挑战赛”题库

为普及消防法律法规和消防安全知识&#xff0c;提升全员消防安全意识&#xff0c;提高抗御火灾、自防自救和组织疏散能力&#xff0c;集团公司近日部署11月份集中开展消防宣传月活动。 为“全民消防”营造浓厚氛围&#xff0c;集团公司以消防知识为主要内容&#xff0c;整理形…

关于如何快速学好,学懂Linux内核。内含学习路线

学习linux内核&#xff0c;这个可不像学一门语言&#xff0c;c或者java一个月或者3月你就能精通掌握。学习linux内核是需要一步一步循序渐进&#xff0c;掌握正确的linux内核学习路线对学习至关重要&#xff0c;本篇文章就来分享学习linux内核的一些建议吧。 1. 了解操作系统基…

nginx(六十一)proxy模块(二)修改发往上游的请求

一 修改发往上游的请求 重点&#xff1a; 利用指令更改转发给上游服务器的HTTP报文的内容1) 请求行 --> proxy_method、url、proxy_http_version2&#xff09;请求头 --> proxy_set_header、proxy_pass_request_headers3&#xff09;请求体 --> proxy_pass_request_b…