Flutter实现牛顿摆动画效果的示例代码

目录
  • 前言
  • 实现步骤
    • 1、绘制静态效果
    • 2、加入动画
    • 两个关键点
  • 完整源码
  • 总结

前言

牛顿摆大家应该都不陌生,也叫碰碰球、永动球(理论情况下),那么今天我们用Flutter实现这么一个理论中的永动球,可以作为加载Loading使用。

- 知识点:绘制、动画曲线、多动画状态更新

效果图:

实现步骤

1、绘制静态效果

首先我们需要把线和小圆球绘制出来,对于看过我之前文章的小伙伴来说这个就很简单了,效果图:

关键代码:

// 小圆球半径
double radius = 6;

/// 小球圆心和直线终点一致
//左边小球圆心
Offset offset = Offset(20, 60);
//右边小球圆心
Offset offset2 = Offset(20 * 6 * 8, 60);

Paint paint = Paint()
  ..color = Colors.black87
  ..strokeWidth = 2;

/// 绘制线
canvas.drawLine(Offset.zero, Offset(90, 0), paint);
canvas.drawLine(Offset(20, 0), offset, paint);
canvas.drawLine(
    Offset(20 + radius * 2, 0), Offset(20 + radius * 2, 60), paint);
canvas.drawLine(
    Offset(20 + radius * 4, 0), Offset(20 + radius * 4, 60), paint);
canvas.drawLine(
    Offset(20 + radius * 6, 0), Offset(20 + radius * 6, 60), paint);
canvas.drawLine(Offset(20 + radius * 8, 0), offset2, paint);

/// 绘制小圆球
canvas.drawCircle(offset, radius, paint);
canvas.drawCircle(Offset(20 + radius * 2, 60), radius, paint);
canvas.drawCircle(Offset(20 + radius * 4, 60), radius, paint);
canvas.drawCircle(Offset(20 + radius * 6, 60), radius, paint);
canvas.drawCircle(offset2, radius, paint);

2、加入动画

思路: 我们可以看到5个小球一共2个小球在运动,左边小球运动一个来回之后传递给右边小球,右边小球开始运动,右边一个来回再传递给左边开始,也就是左边运动周期是:0-1-0,正向运动一次,反向再运动一次,这样就是一个周期,右边也是一样,左边运动完传递给右边,右边运动完传递给左边,这样就简单实现了牛顿摆的效果。

两个关键点

小球运动路径: 小球的运动路径是一个弧度,以竖线的起点为圆心,终点为半径,那么我们只需要设置小球运动至最高点的角度即可,通过角度就可计算出小球的坐标点。

运动曲线: 当然我们知道牛顿摆小球的运动曲线并不是匀速的,他是有一个加速减速过程的,撞击之后,小球先加速然后减速达到最高点速度为0,之后速度再从0慢慢加速进行撞击小球,周而复始。

下面的运动曲线就是先加速再减速,大概符合牛顿摆的运动曲线。我们就使用这个曲线看看效果。

完整源码

class OvalLoading extends StatefulWidget {
  const OvalLoading({Key? key}) : super(key: key);

  @override
  _OvalLoadingState createState() => _OvalLoadingState();
}

class _OvalLoadingState extends State<OvalLoading>
    with TickerProviderStateMixin {
  // 左边小球
  late AnimationController _controller =
      AnimationController(vsync: this, duration: Duration(milliseconds: 300))
        ..addStatusListener((status) {
          if (status == AnimationStatus.completed) {
            _controller.reverse(); //反向执行 1-0
          } else if (status == AnimationStatus.dismissed) {
            _controller2.forward();
          }
        })
        ..forward();
  // 右边小球
  late AnimationController _controller2 =
      AnimationController(vsync: this, duration: Duration(milliseconds: 300))
        ..addStatusListener((status) {
          // dismissed 动画在起始点停止
          // forward 动画正在正向执行
          // reverse 动画正在反向执行
          // completed 动画在终点停止
          if (status == AnimationStatus.completed) {
            _controller2.reverse(); //反向执行 1-0
          } else if (status == AnimationStatus.dismissed) {
            // 反向执行完毕左边小球执行
            _controller.forward();
          }
        });
  late var cure =
      CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic);
  late var cure2 =
      CurvedAnimation(parent: _controller2, curve: Curves.easeOutCubic);

  late Animation<double> animation = Tween(begin: 0.0, end: 1.0).animate(cure);

  late Animation<double> animation2 =
      Tween(begin: 0.0, end: 1.0).animate(cure2);

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: EdgeInsetsDirectional.only(top: 300, start: 150),
      child: CustomPaint(
        size: Size(100, 100),
        painter: _OvalLoadingPainter(
            animation, animation2, Listenable.merge([animation, animation2])),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    _controller2.dispose();
    super.dispose();
  }
}

class _OvalLoadingPainter extends CustomPainter {
  double radius = 6;
  final Animation<double> animation;
  final Animation<double> animation2;
  final Listenable listenable;

  late Offset offset; // 左边小球圆心
  late Offset offset2; // 右边小球圆心

  final double lineLength = 60; // 线长

  _OvalLoadingPainter(this.animation, this.animation2, this.listenable)
      : super(repaint: listenable) {
    offset = Offset(20, lineLength);
    offset2 = Offset(20 * radius * 8, lineLength);
  }

  // 摆动角度
  double angle = pi / 180 * 30; // 30°

  @override
  void paint(Canvas canvas, Size size) {
    Paint paint = Paint()
      ..color = Colors.black87
      ..strokeWidth = 2;

    // 左边小球 默认坐标 下方是90度 需要+pi/2
    var dx = 20 + 60 * cos(pi / 2 + angle * animation.value);
    var dy = 60 * sin(pi / 2 + angle * animation.value);
    // 右边小球
    var dx2 = 20 + radius * 8 - 60 * cos(pi / 2 + angle * animation2.value);
    var dy2 = 60 * sin(pi / 2 + angle * animation2.value);

    offset = Offset(dx, dy);
    offset2 = Offset(dx2, dy2);

    /// 绘制线
      canvas.drawLine(Offset.zero, Offset(90, 0), paint);
    canvas.drawLine(Offset(20, 0), offset, paint);
    canvas.drawLine(
        Offset(20 + radius * 2, 0), Offset(20 + radius * 2, 60), paint);
    canvas.drawLine(
        Offset(20 + radius * 4, 0), Offset(20 + radius * 4, 60), paint);
    canvas.drawLine(
        Offset(20 + radius * 6, 0), Offset(20 + radius * 6, 60), paint);
    canvas.drawLine(Offset(20 + radius * 8, 0), offset2, paint);

    /// 绘制球
    canvas.drawCircle(offset, radius, paint);
    canvas.drawCircle(
        Offset(20 + radius * 2, 60),
        radius,
        paint);

    canvas.drawCircle(Offset(20 + radius * 4, 60), radius, paint);
    canvas.drawCircle(Offset(20 + radius * 6, 60), radius, paint);
    canvas.drawCircle(offset2, radius, paint);
  }
  @override
  bool shouldRepaint(covariant _OvalLoadingPainter oldDelegate) {
    return oldDelegate.listenable != listenable;
  }
}

去掉线的效果

总结

本文展示了实现牛顿摆的原理,其实并不复杂,关键点就是小球的运动轨迹和运动速度曲线,如果用到项目中当做Loading还有很多优化的空间,比如加上小球影子、修改小球颜色或者把小球换成好玩的图片等等操作会看起来更好看一点

到此这篇关于Flutter实现牛顿摆动画效果的示例代码的文章就介绍到这了,更多相关Flutter牛顿摆动画内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Flutter实现心动的动画特效

    目录 实现动画 混入 SingleTickerProviderStateMixin 创建动画 抽离成小组件 完整代码 为了追求更好的用户体验,有时候我们需要一个类似心跳一样跳动着的控件来吸引用户的注意力,这是一个小小的优化需求,但是在 Flutter 里动画两件套就像裹脚布一样臭长,所以需要像封装一个 AnimatedWidget,解放生产力. 实现动画 混入 SingleTickerProviderStateMixin 当创建一个 AnimationController 时,需要传递一个vsy

  • 在Flutter中制作翻转卡片动画的完整实例代码

    目录 前言 使用自写代码 预览 完整代码 使用第三个插件 编码 结论 前言 本文将带您了解在 Flutter 中制作翻转卡片动画的两个完整示例.第一个示例从头开始实现,第二个示例使用第三方包.闲话少说,让我们动手吧. 使用自写代码 本示例使用变换小部件创建翻转卡片效果. 预览 我们将要构建的演示应用程序显示了两张隐藏一些秘密的卡片.您可以通过按"揭示秘密" 按钮来揭开面具背后的东西.最上面的卡片展示了一个水平翻转动画,底部一张展示了一个垂直翻转动画. 完整代码 // main.dart

  • 利用Flutter实现“孔雀开屏”的动画效果

    前言 今天分享一个类似"孔雀开屏"的动画效果,打开新的页面时,新的页面从屏幕右上角以圆形逐渐打开到全屏. 先来看下具体的效果 不知道这种效果大家叫什么名字?如果有更合适的名字可以在评论处告诉我,下面来说下如何实现此效果. 在使用Navigator进入一个新的页面时,通常用法如下: Navigator.of(context).push(MaterialPageRoute( builder: (context){ return PageB(); } )); MaterialPageRout

  • Flutter实现抽屉动画

    这篇会深化View拖拽实例,利用Flutter Animation.插值器以及AnimatedBuilder教大家实现带动画的抽屉效果.先来看效果: 通过构思,我们可以设想到实现抽屉的方式就是用Stack控件将两个Widget叠加显示,用GestureDetector监听手势滑动,动态移动顶层的Widget,当监听到手势结束的时候根据手势滑动的距离动态将顶部Widget利用动画效果滑动到结束位置即可. 实现底部Widget class DownDrawerWidget extends State

  • Android Flutter实现3D动画效果示例详解

    目录 前言 AnimatedWidget 简介 3D 旋转动画的实现 总结 前言 上一篇我们介绍了 Animation 和 AnimationController 的使用,这是最基本的动画构建类.但是,如果我们想构建一个可复用的动画组件,通过外部参数来控制其动画效果的时候,上一篇的方法就不太合适了.在 Flutter 中提供了 AnimatedWidget 组件用于构建可复用的动画组件.本篇我们用 AnimatedWidget 来实现组件的3D 旋转效果,如下图所示. AnimatedWidge

  • Android Flutter实现五种酷炫文字动画效果详解

    目录 前言 波浪涌动效果 波浪线跳动文字组 彩虹动效 滚动广告牌效果 打字效果 其他效果 自定义效果 总结 前言 偶然逛国外博客,看到了一个介绍文字动画的库,进入 pub 一看,立马就爱上这个动画库了,几乎你能想到的文字动画效果它都有!现在正式给大家安利一下这个库:animated_text_kit.本篇我们介绍几个酷炫的效果,其他的效果大家可以自行查看官网文档使用. 波浪涌动效果 波浪涌动 上面的动画效果只需要下面几行代码,其中loadUntil用于控制波浪最终停留的高度,取值是0-1.0,如

  • Flutter实现牛顿摆动画效果的示例代码

    目录 前言 实现步骤 1.绘制静态效果 2.加入动画 两个关键点 完整源码 总结 前言 牛顿摆大家应该都不陌生,也叫碰碰球.永动球(理论情况下),那么今天我们用Flutter实现这么一个理论中的永动球,可以作为加载Loading使用. - 知识点:绘制.动画曲线.多动画状态更新 效果图: 实现步骤 1.绘制静态效果 首先我们需要把线和小圆球绘制出来,对于看过我之前文章的小伙伴来说这个就很简单了,效果图: 关键代码: // 小圆球半径 double radius = 6; /// 小球圆心和直线终

  • vue中实现弹出层动画效果的示例代码

    1 <template> <div class="home"> <!-- 首先将要过渡的元素用transition包裹,并设置过渡的name --> <transition name="mybox"> <div class="box" v-show="boxshow"></div> </transition> <button @click

  • three.js 实现露珠滴落动画效果的示例代码

    前言 大家好,这里是 CSS 魔法使--alphardex. 本文我们将用three.js来实现一种很酷的光学效果--露珠滴落.我们知道,在露珠从一个物体表面滴落的时候,会产生一种粘着的效果.2D平面中,这种粘着效果其实用css滤镜就可以轻松实现.但是到了3D世界,就没那么简单了,这时我们就得依靠光照来实现,其中涉及到了一个关键算法--光线步进(Ray Marching).以下是最终实现的效果图 撒,哈吉马路由! 准备工作 笔者的 three.js模板 :点击右下角的fork即可复制一份 正片

  • pygame用blit()实现动画效果的示例代码

    pygame的的实现动画的方法有很多,但是都是围绕着表面进行的,也就是说实现动画的方式不同,但是本质其实都是对表面的不同处理方式而已. 原理其实很简单,有点像我们做地铁的时候隧道里的广告一样.我们设置一个窗口.然后让窗口在一个画着很多帧图像的图上面移动,当我们透过这个窗口去观察这幅图的时候,只要窗口沿着一个方向去运动,那么就会产生动画效果. 今天我介绍的是通过块传输的方法去实现. surface.blit(image,(x,y),rect)  在这里surface.blit()这个方法应该大家都

  • 使用JavaScript 实现时间轴与动画效果的示例代码(前端组件化)

    目录 代码整理 JavaScript 中的 "帧" 实现"帧"的方法 1. setInterval 2. setTimeout 3. requestAnimationFrame 实现 Timeline 时间轴 实现 start 函数 实现 Animation 类 设计时间线的更新 添加 Delay 属性支持 实现暂停和重启功能 实现 Pause 实现 Resume 上一篇文章<用 JSX 实现 Carousel 轮播组件>中,我们实现了一个 "

  • JavaScript实现扯网动画效果的示例代码

    目录 演示 技术栈 源码 css控制 js部分 演示 技术栈 JavaScript prototype(原型对象): 所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法.Date 对象从 Date.prototype 继承. Array 对象从 Array.prototype 继承. Person 对象从 Person.prototype 继承. 所有 JavaScript 中的对象都是位于原型链顶端的 Object 的实例. JavaScript 对象

  • jquery Ajax 实现加载数据前动画效果的示例代码

    复制代码 代码如下: $(document).ready(function(){     $.ajax({        type:"get",        cache:false,        url:"ajaxpage.aspx?t=getcity",        dataType:"json",        beforeSend:function(){           $("#vvv").append('&l

  • Android Flutter实现点赞效果的示例代码

    目录 前言 绘制小手 完整源码 前言 点赞这个动作不得不说在社交.短视频等App中实在是太常见了,当用户手指按下去的那一刻,给用户一个好的反馈效果也是非常重要的,这样用户点起赞来才会有一种强烈的我点了赞的效果,那么今天我们就用Flutter实现一个掘金App上的点赞效果. 首先我们看下掘金App的点赞组成部分,有一个小手,点赞数字.点赞气泡效果,还有一个震动反馈,接下来我们一步一步实现. 知识点:绘制.动画.震动反馈 绘制小手 这里我们使用Flutter的Icon图标中的点赞小手,Icons图标

  • Flutter进阶之实现动画效果(九)

    在上一篇文章中,我们实现了统计每个产品和地区的销售额,如果现在需要统计每个产品和地区所占市场份额的百分比,那么使用堆叠条形图是不合适的,我们可以使用分组条形图,因为它可以同时在两个类别维度上进行定量比较.分组条形图的实际效果如下图所示: 要实现上面的效果,我们需要更新bar.dart文件的代码: import 'package:flutter/material.dart'; import 'package:flutter/animation.dart'; import 'dart:ui' sho

  • Android 自定义加载动画Dialog弹窗效果的示例代码

    效果图 首先是创建弹窗的背景 这是上面用到的 以shape_bg_5_blue.xml为例,其他的三个无非就是里面的颜色不一样而已 <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <corners android:radius="5dp"

随机推荐