Flutter可执行屏幕动画的AnimateView

news2024/10/7 14:23:58

1.让动画使用起来就像使用widget。

2.可自定义动画。

3.内置平移动画。

演示:

 代码:

import 'dart:math';
import 'package:flutter/cupertino.dart';

class AnimateView extends StatefulWidget {
  ///子Widget
  final Widget child;

  ///动画自定义
  final IAnimate? animate;

  ///是否需要每次刷新时都执行动画
  final bool isNeedFlashEveryTime;

  const AnimateView({
    super.key,
    required this.child,
    this.animate,
    this.isNeedFlashEveryTime = false,
  });

  @override
  State<StatefulWidget> createState() => _AnimateViewState();
}

class _AnimateViewState extends State<AnimateView>
    with TickerProviderStateMixin {
  late IAnimate animate;
  late AnimationController controller;
  late Animation animation;

  @override
  void initState() {
    super.initState();
    animate = widget.animate ??
        TranslationAnimate(
            angle: TranslationAnimateDirection.rightToLeft.angle);
    animate.init();
    controller = animate.getAnimationController(this);
    animation = animate.getAnimation(controller, this);
    //启动动画(正向执行)
    controller.forward();
  }

  @override
  void didUpdateWidget(covariant AnimateView oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.isNeedFlashEveryTime) {
      animate = widget.animate ??
          TranslationAnimate(
              angle: TranslationAnimateDirection.rightToLeft.angle);
      animate.init();
      controller = animate.getAnimationController(this);
      animation = animate.getAnimation(controller, this);
      //启动动画(正向执行)
      controller.forward();
    }
  }

  @override
  Widget build(BuildContext context) {
    return animate.animate(context, widget.child, animation, controller);
  }
}

///动画抽象类。
///实现该类,定制自己的动画。
abstract class IAnimate {
  ///初始化
  void init();

  ///获取AnimationController
  AnimationController getAnimationController(TickerProvider provider);

  ///获取Animation
  Animation getAnimation(
      AnimationController controller, State<StatefulWidget> state);

  ///定制自己的动画,每一个item都会调用到animate,
  ///[widget] 执行动画之后的widget
  ///[index] 列表的item的index
  Widget animate(
    BuildContext context,
    Widget widget,
    Animation animation,
    AnimationController controller,
  );
}

///平移动画
class TranslationAnimate extends IAnimate {
  ///动画执行的总长度
  static double gap = 1000.0;

  ///动画执行角度
  final int angle;

  ///动画执行时长,毫秒(ms)
  final int duration;

  ///进入动画还是出去动画
  final TranslationAnimateType type;

  ///界面的宽,动画需要根据你需要操作的界面宽进行计算,不传代表屏幕宽
  final double? width;

  ///界面的高,动画需要根据你需要操作的界面高进行计算,不传代表屏幕高
  final double? height;

  TranslationAnimate({
    this.angle = 0,
    this.duration = 500,
    this.type = TranslationAnimateType.translateIn,
    this.width,
    this.height,
  });

  @override
  Widget animate(BuildContext context, Widget widget, Animation animation,
      AnimationController controller) {
    final size = MediaQuery.of(context).size;
    double width = this.width ?? size.width;
    double height = this.height ?? size.height;
    double left = 0;
    double top = 0;

    ///范围0.0->1000.0
    double animateValue = animation.value;
    int positiveAngle = angle;
    if (angle < 0) {
      int tempAngle = angle % 360;
      positiveAngle = 360 - tempAngle;
    }
    positiveAngle = positiveAngle % 360;

    ///范围0->1
    double rate = animateValue / gap;
    if (type == TranslationAnimateType.translateIn) {
      rate = rate - 1;
    }
    if (positiveAngle >= 0 && positiveAngle <= 45 ||
        positiveAngle >= 135 && positiveAngle <= 225 ||
        positiveAngle > 315 && positiveAngle <= 360) {
      ///移出距离以宽度为准
      left = rate * width;
      if (positiveAngle > 90 && positiveAngle < 270) {
        left = -left;
      }
      double tanValue = tan(positiveAngle * pi / 180);
      top = rate * width * tanValue;
    } else if (positiveAngle == 90) {
      top = rate * height;
      left = 0;
    } else if (positiveAngle == 270) {
      top = -rate * height;
      left = 0;
    } else {
      ///移出距离以高度为准
      top = rate * height;
      if (positiveAngle > 180 && positiveAngle < 360) {
        top = -top;
      }
      double tanValue = tan(positiveAngle * pi / 180);
      if (tanValue == 0) {
        left = 0;
      } else {
        left = rate * height / tanValue;
      }
    }

    //print("angle=$positiveAngle");
    //print("left=$left");
    //print("top=$top");

    return Container(
      transform: Matrix4.translationValues(left, top, 0),
      child: widget,
    );
  }

  @override
  Animation getAnimation(
      AnimationController controller, State<StatefulWidget> state) {
    return Tween(begin: 0.0, end: gap).animate(controller)
      ..addListener(() {
        if (state.mounted) {
          state.setState(() {});
        }
      });
  }

  @override
  AnimationController getAnimationController(TickerProvider provider) {
    return AnimationController(
        duration: Duration(milliseconds: duration), vsync: provider);
  }

  @override
  void init() {}
}

///平移动画执行方向枚举
enum TranslationAnimateDirection {
  ///从下往上
  bottomToTop(90),

  ///从上往下
  topToBottom(270),

  ///从左往右
  leftToRight(0),

  ///从右往左
  rightToLeft(180);

  ///动画执行方向度数
  final int angle;

  const TranslationAnimateDirection(this.angle);
}

///位移动画类型
enum TranslationAnimateType {
  ///出去动画
  translateOut,

  ///进入动画
  translateIn
}

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

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

相关文章

什么,一条指令直接黑了数据库!

什么&#xff0c;一条指令直接黑了数据库&#xff01; shigen最近研究了一下一款渗透工具sqlMap。它一款流行的开源工具&#xff0c;用于自动化SQL注入攻击和渗透测试。它专门设计用于检测和利用Web应用程序中的SQL注入漏洞。SQLMap具有丰富的功能集&#xff0c;可自动检测和利…

工厂方法模式的概述和使用

目录 一、工厂方法模式概述1. 定义2. 使用动机 二、工厂方法模式结构1. 模式结构2. 时序图 三、工厂方法模式的使用实例四、工厂方法模式的优缺点五、工厂方法模式在Java中应用 原文链接 一、工厂方法模式概述 1. 定义 工厂方法模式(Factory Method Pattern)又称为工厂模式&…

高通面临难题,Oryon核心存在问题,高通8cx Gen 4芯片将推迟发布

"高通公司面临难题&#xff0c;可能会导致骁龙8cx Gen 4的发布时间推迟"&#xff0c;关于骁龙8cx Gen 4处理器&#xff0c;还有一些其他值得关注的特点和功能。首先&#xff0c;据悉&#xff0c;骁龙8cx Gen 4采用了高通自家研发的Oryon核心架构&#xff0c;这是一项…

春秋云镜 Brute4Road

flag1 fscan扫描发现&#xff0c;6379开放ftp可以匿名登录 这里直接尝试了去打redis但是只有主从复制能成功&#xff08;这里应该是靶场有设置吧&#xff0c;对6379操作过后再次操作就会显示端口拒绝访问直接重置就可以了&#xff09; 之后用脚本一把梭哈即可获得shell #更改…

C++ list模拟实现

list模拟实现代码&#xff1a; namespace djx {template<class T>struct list_node{T _data;list_node<T>* _prev;list_node<T>* _next;list_node(const T& x T()):_data(x),_prev(nullptr),_next(nullptr){}};template<class T,class Ref,class Pt…

好用的网页制作工具就是这6个,快点来看!

对于网页设计师来说&#xff0c;好用的网页设计工具是非常重要的&#xff0c;今天本文收集了6个好用的网页设计工具供设计师自由挑选使用。在这6个好用的网页设计工具的帮助下&#xff0c;设计师将获得更高的工作效率和更精致的网页设计效果&#xff0c;接下来&#xff0c;就一…

Android studio APK切换多个摄像头(Camera2)

1.先设置camera的权限 <uses-permission android:name"android.permission.CAMERA" /> 2.布局 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"and…

vue过渡及动画

文章目录 前言类名使用自己定义动画样式多个元素过渡使用第三方库 前言 对于vue中的过渡与动画&#xff0c;官网上是这样概述的&#xff1a; Vue 在插入、更新或者移除 DOM 时&#xff0c;提供多种不同方式的应用过渡效果。包括以下工具&#xff1a; 在 CSS 过渡和动画中自动…

基于空洞卷积DCNN与长短期时间记忆模型LSTM的dcnn-lstm的回归预测模型

周末的时候有时间鼓捣的一个小实践&#xff0c;主要就是做的多因子回归预测的任务&#xff0c;关于时序数据建模和回归预测建模我的专栏和系列博文里面已经有了非常详细的介绍了&#xff0c;这里就不再多加赘述了&#xff0c;这里主要是一个模型融合的实践&#xff0c;这里的数…

避雷!教你正确区分流量卡,不看可别后悔!

分不清真假流量卡&#xff1f; 想要手机流量卡&#xff0c;不小心买到了物联卡&#xff0c;结果被商家割了韭菜&#xff1f; 对于流量卡的套路太多了&#xff1f;你是否还傻傻分不清楚&#xff0c;今天&#xff0c;这篇文章教你正确区分这两种不同类型的卡。 ​ 赶紧收藏&am…

文件重命名与隐藏编号一键搞定!让不同类型的文件整齐有序

大家好&#xff01;在整理和管理不同类型的文件时&#xff0c;我们经常遇到文件名不规范、编号杂乱的情况&#xff0c;使得文件整体显得混乱无序。为了帮助您达到整齐有序的效果&#xff0c;我们自豪地推出了一款高效的工具——文件重命名与隐藏编号软件&#xff01;让您能够轻…

Redis进阶 - JVM进程缓存

原文首更地址&#xff0c;阅读效果更佳&#xff01; Redis进阶 - JVM进程缓存 | CoderMast编程桅杆https://www.codermast.com/database/redis/redis-advance-jvm-process-cache.html 传统缓存的问题 传统的缓存策略一般是请求到达 Tomcat 后&#xff0c;先查询 Redis &…

Qt开发实例_实时监测磁盘剩余空间

一、前言 当计算机磁盘空间不足,会导致应用程序无法继续存储数据,导致错误和不稳定的情况。所以,实时检测磁盘空间剩余大小对于许多应用程序来说都非常重要。 这种需求在许多应用程序中都存在,例如文件管理器、图像编辑器、视频播放器、云存储服务等等。在这些应用程序中…

Python+Appium自动化测试-编写自动化脚本

之前已经讲述怎样手动使用appium-desktop启动测试机上的app&#xff0c;但我们实际跑自动化脚本的过程中&#xff0c;是需要用脚本调用appium启动app的&#xff0c;接下来就尝试写Python脚本启动app并登陆app。环境为Windows10 Python3.7 appium1.18.0 Android手机 今日头条…

Apex导航菜单权限动态分配的实现

改动之处 return is_have_permission_wxx(V(WXX_USER_ID), 2,V(WXX_ROLE_ID)); 改之后 return is_have_permission_wxx(V(USER_ID), 2,V(ROLE_ID)); 创建新的动态菜单“动态菜单1” 共享组件→列表→创建→动态 selectlevel,m1.NAME label,f?p&||APP_ID.:||m1.…

17-会话机制

cookie 和 session cookie存储在本地 session是在服务器端进行用户信息的保存,一个字典 Cookie: # 1. 设置 : 通过response对象设置response redirect(xxx)response render_template(xxx)response Response(xxx)response make_response(xxx)response jsonify(xxx)# 通过…

xxl-job学习(一篇文章解决)

前言&#xff1a;学习xxl-job需要有git&#xff0c;springboot的基础&#xff0c;学起来就很简单 xxl-job是一个分布式的任务调度平台&#xff0c;其核心设计目标是&#xff1a;学习简单、开发迅速、轻量级、易扩展&#xff0c;现在已经开放源代码并接入多家公司的线上产品线&a…

详解过滤器Filter和拦截器Interceptor的区别和联系

目录 前言 区别 联系 前言 过滤器(Filter)和拦截器(Interceptor)都是用于在Web应用程序中处理请求和响应的组件&#xff0c;但它们在实现方式和功能上有一些区别。 区别 1. 实现方式&#xff1a; - 过滤器是基于Servlet规范的组件&#xff0c;通过实现javax.servlet.Filt…

热烈祝贺重庆融能成功入选航天系统采购供应商库

经过航天系统采购平台的严审&#xff0c;重庆融能机电设备股份有限公司成功入选中国航天系统采购供应商库。航天系统采购平台是航天系统内企业采购专用平台&#xff0c;服务航天全球范围千亿采购需求&#xff0c;目前&#xff0c;已有华为、三一重工、格力电器、科大讯飞等企业…

酒店资产如何管理提升资产利用效率

酒店资产管理系统是一种专门为酒店行业设计的管理软件&#xff0c;可以帮助酒店实现资产的全生命周期管理。一个好的酒店资产管理系统应该具备以下功能&#xff1a;  资产登记&#xff1a;可以对酒店的各种资产进行登记&#xff0c;包括房间、家具、设备等&#xff0c;记录资…